From a01857ecd5b6e1b0acb9f51a8230375c87c03f51 Mon Sep 17 00:00:00 2001 From: Gabriel Weyer Date: Sun, 17 Nov 2013 08:34:23 +1100 Subject: [PATCH] Implemented pull request review comments API --- ...servablePullRequestReviewCommentsClient.cs | 90 ++++ ...servablePullRequestReviewCommentsClient.cs | 157 ++++++ Octokit.Reactive/Octokit.Reactive.csproj | 4 +- .../PullRequestReviewCommentsClientTests.cs | 312 ++++++++++++ Octokit.Tests/Octokit.Tests.csproj | 2 + ...blePullRequestReviewCommentsClientTests.cs | 456 ++++++++++++++++++ .../IPullRequestReviewCommentsClient.cs | 90 ++++ Octokit/Clients/IPullRequestsClient.cs | 11 + .../PullRequestReviewCommentsClient.cs | 149 ++++++ Octokit/Clients/PullRequestsClient.cs | 16 + Octokit/GitHubClient.cs | 2 + Octokit/Helpers/ApiUrls.cs | 35 ++ Octokit/IGitHubClient.cs | 1 + .../Request/PullRequestReviewCommentCreate.cs | 28 ++ .../Request/PullRequestReviewCommentEdit.cs | 11 + .../PullRequestReviewCommentReplyCreate.cs | 18 + .../PullRequestReviewCommentRequest.cs | 31 ++ .../Response/PullRequestReviewComment.cs | 118 +++++ Octokit/Octokit-Mono.csproj | 9 + Octokit/Octokit-MonoAndroid.csproj | 9 + Octokit/Octokit-Monotouch.csproj | 9 + Octokit/Octokit-netcore45.csproj | 9 + Octokit/Octokit.csproj | 11 +- 23 files changed, 1576 insertions(+), 2 deletions(-) create mode 100644 Octokit.Reactive/Clients/IObservablePullRequestReviewCommentsClient.cs create mode 100644 Octokit.Reactive/Clients/ObservablePullRequestReviewCommentsClient.cs create mode 100644 Octokit.Tests/Clients/PullRequestReviewCommentsClientTests.cs create mode 100644 Octokit.Tests/Reactive/ObservablePullRequestReviewCommentsClientTests.cs create mode 100644 Octokit/Clients/IPullRequestReviewCommentsClient.cs create mode 100644 Octokit/Clients/IPullRequestsClient.cs create mode 100644 Octokit/Clients/PullRequestReviewCommentsClient.cs create mode 100644 Octokit/Clients/PullRequestsClient.cs create mode 100644 Octokit/Models/Request/PullRequestReviewCommentCreate.cs create mode 100644 Octokit/Models/Request/PullRequestReviewCommentEdit.cs create mode 100644 Octokit/Models/Request/PullRequestReviewCommentReplyCreate.cs create mode 100644 Octokit/Models/Request/PullRequestReviewCommentRequest.cs create mode 100644 Octokit/Models/Response/PullRequestReviewComment.cs diff --git a/Octokit.Reactive/Clients/IObservablePullRequestReviewCommentsClient.cs b/Octokit.Reactive/Clients/IObservablePullRequestReviewCommentsClient.cs new file mode 100644 index 00000000..89526242 --- /dev/null +++ b/Octokit.Reactive/Clients/IObservablePullRequestReviewCommentsClient.cs @@ -0,0 +1,90 @@ +using System; +using System.Reactive; + +namespace Octokit.Reactive +{ + public interface IObservablePullRequestReviewCommentsClient + { + /// + /// Gets review comments for a specified pull request. + /// + /// http://developer.github.com/v3/pulls/comments/#list-comments-on-a-pull-request + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The list of s for the specified pull request + IObservable GetForPullRequest(string owner, string name, int number); + + /// + /// Gets a list of the pull request review comments in a specified repository. + /// + /// http://developer.github.com/v3/pulls/comments/#list-comments-in-a-repository + /// The owner of the repository + /// The name of the repository + /// The list of s for the specified repository + IObservable GetForRepository(string owner, string name); + + /// + /// Gets a list of the pull request review comments in a specified repository. + /// + /// http://developer.github.com/v3/pulls/comments/#list-comments-in-a-repository + /// The owner of the repository + /// The name of the repository + /// The sorting parameters + /// The list of s for the specified repository + IObservable GetForRepository(string owner, string name, PullRequestReviewCommentRequest request); + + /// + /// Gets a single pull request review comment by number. + /// + /// http://developer.github.com/v3/pulls/comments/#get-a-single-comment + /// The owner of the repository + /// The name of the repository + /// The pull request review comment number + /// The + IObservable GetComment(string owner, string name, int number); + + /// + /// Creates a comment on a pull request review. + /// + /// http://developer.github.com/v3/pulls/comments/#create-a-comment + /// The owner of the repository + /// The name of the repository + /// The Pull Request number + /// The comment + /// The created + IObservable Create(string owner, string name, int number, PullRequestReviewCommentCreate comment); + + /// + /// Creates a comment on a pull request review as a reply to another comment. + /// + /// http://developer.github.com/v3/pulls/comments/#create-a-comment + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The comment + /// The created + IObservable CreateReply(string owner, string name, int number, PullRequestReviewCommentReplyCreate comment); + + /// + /// Edits a comment on a pull request review. + /// + /// http://developer.github.com/v3/pulls/comments/#edit-a-comment + /// The owner of the repository + /// The name of the repository + /// The pull request review comment number + /// The edited comment + /// The edited + IObservable Edit(string owner, string name, int number, PullRequestReviewCommentEdit comment); + + /// + /// Deletes a comment on a pull request review. + /// + /// http://developer.github.com/v3/pulls/comments/#delete-a-comment + /// The owner of the repository + /// The name of the repository + /// The pull request review comment number + /// + IObservable Delete(string owner, string name, int number); + } +} diff --git a/Octokit.Reactive/Clients/ObservablePullRequestReviewCommentsClient.cs b/Octokit.Reactive/Clients/ObservablePullRequestReviewCommentsClient.cs new file mode 100644 index 00000000..140bb5a7 --- /dev/null +++ b/Octokit.Reactive/Clients/ObservablePullRequestReviewCommentsClient.cs @@ -0,0 +1,157 @@ +using System; +using System.Reactive; +using System.Reactive.Threading.Tasks; +using Octokit.Reactive.Internal; + +namespace Octokit.Reactive +{ + public class ObservablePullRequestReviewCommentsClient : IObservablePullRequestReviewCommentsClient + { + readonly IPullRequestReviewCommentsClient _client; + readonly IConnection _connection; + + public ObservablePullRequestReviewCommentsClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, "client"); + + _client = client.PullRequest.Comment; + _connection = client.Connection; + } + + /// + /// Gets review comments for a specified pull request. + /// + /// http://developer.github.com/v3/pulls/comments/#list-comments-on-a-pull-request + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The list of s for the specified pull request + public IObservable GetForPullRequest(string owner, string name, int number) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return _connection.GetAndFlattenAllPages(ApiUrls.PullRequestReviewComments(owner, name, number)); + } + + /// + /// Gets a list of the pull request review comments in a specified repository. + /// + /// http://developer.github.com/v3/pulls/comments/#list-comments-in-a-repository + /// The owner of the repository + /// The name of the repository + /// The list of s for the specified repository + public IObservable GetForRepository(string owner, string name) + { + return GetForRepository(owner, name, new PullRequestReviewCommentRequest()); + } + + /// + /// Gets a list of the pull request review comments in a specified repository. + /// + /// http://developer.github.com/v3/pulls/comments/#list-comments-in-a-repository + /// The owner of the repository + /// The name of the repository + /// The sorting parameters + /// The list of s for the specified repository + public IObservable GetForRepository(string owner, string name, PullRequestReviewCommentRequest request) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(request, "request"); + + return _connection.GetAndFlattenAllPages(ApiUrls.PullRequestReviewCommentsRepository(owner, name), request.ToParametersDictionary()); + } + + /// + /// Gets a single pull request review comment by number. + /// + /// http://developer.github.com/v3/pulls/comments/#get-a-single-comment + /// The owner of the repository + /// The name of the repository + /// The pull request review comment number + /// The + public IObservable GetComment(string owner, string name, int number) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return _client.GetComment(owner, name, number).ToObservable(); + } + + /// + /// Creates a comment on a pull request review. + /// + /// http://developer.github.com/v3/pulls/comments/#create-a-comment + /// The owner of the repository + /// The name of the repository + /// The Pull Request number + /// The comment + /// The created + public IObservable Create(string owner, string name, int number, PullRequestReviewCommentCreate comment) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(comment, "comment"); + Ensure.ArgumentNotNullOrEmptyString(comment.Body, "body"); + Ensure.ArgumentNotNullOrEmptyString(comment.CommitId, "commitId"); + Ensure.ArgumentNotNullOrEmptyString(comment.Path, "path"); + + return _client.Create(owner, name, number, comment).ToObservable(); + } + + /// + /// Creates a comment on a pull request review as a reply to another comment. + /// + /// http://developer.github.com/v3/pulls/comments/#create-a-comment + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The comment + /// The created + public IObservable CreateReply(string owner, string name, int number, PullRequestReviewCommentReplyCreate comment) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(comment, "comment"); + Ensure.ArgumentNotNullOrEmptyString(comment.Body, "body"); + + return _client.CreateReply(owner, name, number, comment).ToObservable(); + } + + /// + /// Edits a comment on a pull request review. + /// + /// http://developer.github.com/v3/pulls/comments/#edit-a-comment + /// The owner of the repository + /// The name of the repository + /// The pull request review comment number + /// The edited comment + /// The edited + public IObservable Edit(string owner, string name, int number, PullRequestReviewCommentEdit comment) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(comment, "comment"); + Ensure.ArgumentNotNullOrEmptyString(comment.Body, "body"); + + return _client.Edit(owner, name, number, comment).ToObservable(); + } + + /// + /// Deletes a comment on a pull request review. + /// + /// http://developer.github.com/v3/pulls/comments/#delete-a-comment + /// The owner of the repository + /// The name of the repository + /// The pull request review comment number + /// + public IObservable Delete(string owner, string name, int number) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return _client.Delete(owner, name, number).ToObservable(); + } + } +} diff --git a/Octokit.Reactive/Octokit.Reactive.csproj b/Octokit.Reactive/Octokit.Reactive.csproj index 2a05fe83..340def6a 100644 --- a/Octokit.Reactive/Octokit.Reactive.csproj +++ b/Octokit.Reactive/Octokit.Reactive.csproj @@ -74,6 +74,7 @@ Properties\SolutionInfo.cs + @@ -97,6 +98,7 @@ + @@ -143,4 +145,4 @@ --> - + \ No newline at end of file diff --git a/Octokit.Tests/Clients/PullRequestReviewCommentsClientTests.cs b/Octokit.Tests/Clients/PullRequestReviewCommentsClientTests.cs new file mode 100644 index 00000000..70b0de76 --- /dev/null +++ b/Octokit.Tests/Clients/PullRequestReviewCommentsClientTests.cs @@ -0,0 +1,312 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using NSubstitute; +using Octokit; +using Octokit.Tests.Helpers; +using Xunit; + +public class PullRequestReviewCommentsClientTests +{ + public class TheGetForPullRequestMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new PullRequestReviewCommentsClient(connection); + + client.GetForPullRequest("fakeOwner", "fakeRepoName", 7); + + connection.Received().GetAll(Arg.Is(u => u.ToString() == "repos/fakeOwner/fakeRepoName/pulls/7/comments")); + } + + [Fact] + public async Task EnsuresArgumentsNotNull() + { + var connection = Substitute.For(); + var client = new PullRequestReviewCommentsClient(connection); + + await AssertEx.Throws(async () => await client.GetForPullRequest(null, "name", 1)); + await AssertEx.Throws(async () => await client.GetForPullRequest("", "name", 1)); + await AssertEx.Throws(async () => await client.GetForPullRequest("owner", null, 1)); + await AssertEx.Throws(async () => await client.GetForPullRequest("owner", "", 1)); + } + } + + public class TheGetForRepositoryMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new PullRequestReviewCommentsClient(connection); + + var request = new PullRequestReviewCommentRequest + { + Direction = SortDirection.Descending, + Since = new DateTimeOffset(2013, 11, 15, 11, 43, 01, 00, new TimeSpan()), + Sort = PullRequestReviewCommentSort.Updated, + }; + + client.GetForRepository("fakeOwner", "fakeRepoName", request); + + connection.Received().GetAll(Arg.Is(u => u.ToString() == "repos/fakeOwner/fakeRepoName/pulls/comments"), + Arg.Is>(d => d.Count == 3 + && d["direction"] == "desc" + && d["since"] == "2013-11-15T11:43:01Z" + && d["sort"] == "updated")); + } + + [Fact] + public void RequestsCorrectUrlWithoutSelectedSortingArguments() + { + var connection = Substitute.For(); + var client = new PullRequestReviewCommentsClient(connection); + + client.GetForRepository("fakeOwner", "fakeRepoName"); + + connection.Received().GetAll(Arg.Is(u => u.ToString() == "repos/fakeOwner/fakeRepoName/pulls/comments"), + Arg.Is>(d => d.Count == 2 + && d["direction"] == "asc" + && d["sort"] == "created")); + } + + [Fact] + public async Task EnsuresArgumentsNotNull() + { + var client = new PullRequestReviewCommentsClient(Substitute.For()); + + var request = new PullRequestReviewCommentRequest(); + + await AssertEx.Throws(async () => await client.GetForRepository(null, "name", request)); + await AssertEx.Throws(async () => await client.GetForRepository("", "name", request)); + await AssertEx.Throws(async () => await client.GetForRepository("owner", null, request)); + await AssertEx.Throws(async () => await client.GetForRepository("owner", "", request)); + await AssertEx.Throws(async () => await client.GetForRepository("owner", "name", null)); + } + + [Fact] + public async Task EnsuresDefaultValues() + { + var request = new PullRequestReviewCommentRequest(); + + Assert.Equal(SortDirection.Ascending, request.Direction); + Assert.Null(request.Since); + Assert.Equal(PullRequestReviewCommentSort.Created, request.Sort); + } + } + + public class TheGetCommentMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new PullRequestReviewCommentsClient(connection); + + client.GetComment("fakeOwner", "fakeRepoName", 53); + + connection.Received().Get(Arg.Is(u => u.ToString() == "repos/fakeOwner/fakeRepoName/pulls/comments/53"), + null); + } + + [Fact] + public async Task EnsuresArgumentsNotNull() + { + var client = new PullRequestReviewCommentsClient(Substitute.For()); + + await AssertEx.Throws(async () => await client.GetComment(null, "name", 1)); + await AssertEx.Throws(async () => await client.GetComment("", "name", 1)); + await AssertEx.Throws(async () => await client.GetComment("owner", null, 1)); + await AssertEx.Throws(async () => await client.GetComment("owner", "", 1)); + } + } + + public class TheCreateMethod + { + [Fact] + public void PostsToCorrectUrl() + { + var connection = Substitute.For(); + var client = new PullRequestReviewCommentsClient(connection); + + var comment = new PullRequestReviewCommentCreate + { + Body = "Comment content", + CommitId = "qe3dsdsf6", + Path = "file.css", + Position = 7, + }; + + client.Create("fakeOwner", "fakeRepoName", 13, comment); + + connection.Received().Post(Arg.Is(u => u.ToString() == "repos/fakeOwner/fakeRepoName/pulls/13/comments"), + comment); + } + + [Fact] + public async Task EnsuresArgumentsNotNull() + { + var connection = Substitute.For(); + var client = new PullRequestReviewCommentsClient(connection); + + string body = "Comment content"; + string commitId = "qe3dsdsf6"; + string path = "file.css"; + int position = 7; + + var comment = new PullRequestReviewCommentCreate + { + Body = body, + CommitId = commitId, + Path = path, + Position = position, + }; + + await AssertEx.Throws(async () => await client.Create(null, "fakeRepoName", 1, comment)); + await AssertEx.Throws(async () => await client.Create("", "fakeRepoName", 1, comment)); + await AssertEx.Throws(async () => await client.Create("fakeOwner", null, 1, comment)); + await AssertEx.Throws(async () => await client.Create("fakeOwner", "", 1, comment)); + await AssertEx.Throws(async () => await client.Create("fakeOwner", "fakeRepoName", 1, null)); + + comment.Body = null; + await AssertEx.Throws(async () => await client.Create("fakeOwner", "fakeRepoName", 1, comment)); + comment.Body = ""; + await AssertEx.Throws(async () => await client.Create("fakeOwner", "fakeRepoName", 1, comment)); + comment.Body = body; + + comment.CommitId = null; + await AssertEx.Throws(async () => await client.Create("fakeOwner", "fakeRepoName", 1, comment)); + comment.CommitId = ""; + await AssertEx.Throws(async () => await client.Create("fakeOwner", "fakeRepoName", 1, comment)); + comment.CommitId = commitId; + + comment.Path = null; + await AssertEx.Throws(async () => await client.Create("fakeOwner", "fakeRepoName", 1, comment)); + comment.Path = ""; + await AssertEx.Throws(async () => await client.Create("fakeOwner", "fakeRepoName", 1, comment)); + comment.Path = path; + } + } + + public class TheCreateReplyMethod + { + [Fact] + public void PostsToCorrectUrl() + { + var connection = Substitute.For(); + var client = new PullRequestReviewCommentsClient(connection); + + var comment = new PullRequestReviewCommentReplyCreate + { + Body = "Comment content", + InReplyTo = 5 + }; + + client.CreateReply("fakeOwner", "fakeRepoName", 13, comment); + + connection.Received().Post(Arg.Is(u => u.ToString() == "repos/fakeOwner/fakeRepoName/pulls/13/comments"), + comment); + } + + [Fact] + public async Task EnsuresArgumentsNotNull() + { + var connection = Substitute.For(); + var client = new PullRequestReviewCommentsClient(connection); + + string body = "Comment content"; + int inReplyTo = 7; + + var comment = new PullRequestReviewCommentReplyCreate + { + Body = body, + InReplyTo = inReplyTo, + }; + + await AssertEx.Throws(async () => await client.CreateReply(null, "fakeRepoName", 1, comment)); + await AssertEx.Throws(async () => await client.CreateReply("", "fakeRepoName", 1, comment)); + await AssertEx.Throws(async () => await client.CreateReply("fakeOwner", null, 1, comment)); + await AssertEx.Throws(async () => await client.CreateReply("fakeOwner", "", 1, comment)); + await AssertEx.Throws(async () => await client.CreateReply("fakeOwner", "fakeRepoName", 1, null)); + + comment.Body = null; + await AssertEx.Throws(async () => await client.CreateReply("fakeOwner", "fakeRepoName", 1, comment)); + comment.Body = ""; + await AssertEx.Throws(async () => await client.CreateReply("fakeOwner", "fakeRepoName", 1, comment)); + comment.Body = body; + } + } + + public class TheEditMethod + { + [Fact] + public void PostsToCorrectUrl() + { + var connection = Substitute.For(); + var client = new PullRequestReviewCommentsClient(connection); + + var comment = new PullRequestReviewCommentEdit + { + Body = "New comment content", + }; + + client.Edit("fakeOwner", "fakeRepoName", 13, comment); + + connection.Received().Patch(Arg.Is(u => u.ToString() == "repos/fakeOwner/fakeRepoName/pulls/comments/13"), comment); + } + + [Fact] + public async Task EnsuresArgumentsNotNull() + { + var connection = Substitute.For(); + var client = new PullRequestReviewCommentsClient(connection); + + var body = "New comment content"; + + var comment = new PullRequestReviewCommentEdit + { + Body = body, + }; + + await AssertEx.Throws(async () => await client.Edit(null, "fakeRepoName", 1, comment)); + await AssertEx.Throws(async () => await client.Edit("", "fakeRepoName", 1, comment)); + await AssertEx.Throws(async () => await client.Edit("fakeOwner", null, 1, comment)); + await AssertEx.Throws(async () => await client.Edit("fakeOwner", "", 1, comment)); + await AssertEx.Throws(async () => await client.Edit("fakeOwner", null, 1, null)); + + comment.Body = null; + await AssertEx.Throws(async () => await client.Edit("fakeOwner", "fakeRepoName", 1, comment)); + comment.Body = ""; + await AssertEx.Throws(async () => await client.Edit("fakeOwner", "fakeRepoName", 1, comment)); + comment.Body = body; + } + } + + public class TheDeleteMethod + { + [Fact] + public void PostsToCorrectUrl() + { + var connection = Substitute.For(); + var client = new PullRequestReviewCommentsClient(connection); + + client.Delete("fakeOwner", "fakeRepoName", 13); + + connection.Received().Delete(Arg.Is(u => u.ToString() == "repos/fakeOwner/fakeRepoName/pulls/comments/13")); + } + + [Fact] + public async Task EnsuresArgumentsNotNull() + { + var connection = Substitute.For(); + var client = new PullRequestReviewCommentsClient(connection); + + await AssertEx.Throws(async () => await client.Delete(null, "fakeRepoName", 1)); + await AssertEx.Throws(async () => await client.Delete("", "fakeRepoName", 1)); + await AssertEx.Throws(async () => await client.Delete("fakeOwner", null, 1)); + await AssertEx.Throws(async () => await client.Delete("fakeOwner", "", 1)); + } + } +} diff --git a/Octokit.Tests/Octokit.Tests.csproj b/Octokit.Tests/Octokit.Tests.csproj index cff9ef07..2450c067 100644 --- a/Octokit.Tests/Octokit.Tests.csproj +++ b/Octokit.Tests/Octokit.Tests.csproj @@ -66,6 +66,7 @@ + @@ -122,6 +123,7 @@ + diff --git a/Octokit.Tests/Reactive/ObservablePullRequestReviewCommentsClientTests.cs b/Octokit.Tests/Reactive/ObservablePullRequestReviewCommentsClientTests.cs new file mode 100644 index 00000000..6d1d3e98 --- /dev/null +++ b/Octokit.Tests/Reactive/ObservablePullRequestReviewCommentsClientTests.cs @@ -0,0 +1,456 @@ +using System; +using System.Collections.Generic; +using System.Reactive.Linq; +using System.Threading.Tasks; +using NSubstitute; +using Octokit.Internal; +using Octokit.Reactive; +using Octokit.Tests.Helpers; +using Xunit; + +namespace Octokit.Tests.Reactive +{ + public class ObservablePullRequestReviewCommentsClientTests + { + static ApiInfo CreateApiInfo(IDictionary links) + { + return new ApiInfo(links, new List(), new List(), "etag", new RateLimit(new Dictionary())); + } + + public class TheGetForPullRequestMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var firstPageUrl = new Uri("repos/fakeOwner/fakeRepoName/pulls/7/comments", UriKind.Relative); + var secondPageUrl = new Uri("https://example.com/page/2"); + var firstPageLinks = new Dictionary { { "next", secondPageUrl } }; + var firstPageResponse = new ApiResponse> + { + BodyAsObject = new List + { + new PullRequestReviewComment {Id = 1}, + new PullRequestReviewComment {Id = 2}, + new PullRequestReviewComment {Id = 3} + }, + ApiInfo = CreateApiInfo(firstPageLinks) + }; + var thirdPageUrl = new Uri("https://example.com/page/3"); + var secondPageLinks = new Dictionary { { "next", thirdPageUrl } }; + var secondPageResponse = new ApiResponse> + { + BodyAsObject = new List + { + new PullRequestReviewComment {Id = 4}, + new PullRequestReviewComment {Id = 5}, + new PullRequestReviewComment {Id = 6} + }, + ApiInfo = CreateApiInfo(secondPageLinks) + }; + var lastPageResponse = new ApiResponse> + { + BodyAsObject = new List + { + new PullRequestReviewComment {Id = 7} + }, + ApiInfo = CreateApiInfo(new Dictionary()) + }; + + var gitHubClient = Substitute.For(); + gitHubClient.Connection.GetAsync>(firstPageUrl) + .Returns(Task.Factory.StartNew>>(() => firstPageResponse)); + gitHubClient.Connection.GetAsync>(secondPageUrl) + .Returns(Task.Factory.StartNew>>(() => secondPageResponse)); + gitHubClient.Connection.GetAsync>(thirdPageUrl) + .Returns(Task.Factory.StartNew>>(() => lastPageResponse)); + + var client = new ObservablePullRequestReviewCommentsClient(gitHubClient); + + var results = await client.GetForPullRequest("fakeOwner", "fakeRepoName", 7).ToArray(); + + Assert.Equal(7, results.Length); + gitHubClient.Connection.Received(1).GetAsync>(firstPageUrl, null, null); + gitHubClient.Connection.Received(1).GetAsync>(secondPageUrl, null, null); + gitHubClient.Connection.Received(1).GetAsync>(thirdPageUrl, null, null); + } + + [Fact] + public async Task EnsuresArgumentsNotNull() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewCommentsClient(gitHubClient); + + await AssertEx.Throws(async () => await client.GetForPullRequest(null, "name", 1)); + await AssertEx.Throws(async () => await client.GetForPullRequest("", "name", 1)); + await AssertEx.Throws(async () => await client.GetForPullRequest("owner", null, 1)); + await AssertEx.Throws(async () => await client.GetForPullRequest("owner", "", 1)); + await AssertEx.Throws(async () => await client.GetForPullRequest(null, null, 1)); + await AssertEx.Throws(async () => await client.GetForPullRequest("", "", 1)); + } + } + + public class TheGetForRepositoryMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var firstPageUrl = new Uri("repos/fakeOwner/fakeRepoName/pulls/comments", UriKind.Relative); + var secondPageUrl = new Uri("https://example.com/page/2"); + var firstPageLinks = new Dictionary { { "next", secondPageUrl } }; + var firstPageResponse = new ApiResponse> + { + BodyAsObject = new List + { + new PullRequestReviewComment {Id = 1}, + new PullRequestReviewComment {Id = 2}, + new PullRequestReviewComment {Id = 3} + }, + ApiInfo = CreateApiInfo(firstPageLinks) + }; + var thirdPageUrl = new Uri("https://example.com/page/3"); + var secondPageLinks = new Dictionary { { "next", thirdPageUrl } }; + var secondPageResponse = new ApiResponse> + { + BodyAsObject = new List + { + new PullRequestReviewComment {Id = 4}, + new PullRequestReviewComment {Id = 5}, + new PullRequestReviewComment {Id = 6} + }, + ApiInfo = CreateApiInfo(secondPageLinks) + }; + var lastPageResponse = new ApiResponse> + { + BodyAsObject = new List + { + new PullRequestReviewComment {Id = 7}, + new PullRequestReviewComment {Id = 8}, + }, + ApiInfo = CreateApiInfo(new Dictionary()) + }; + + var gitHubClient = Substitute.For(); + + gitHubClient.Connection.GetAsync>(firstPageUrl, + Arg.Is>(d => d.Count == 3 + && d["direction"] == "desc" + && d["since"] == "2013-11-15T11:43:01Z" + && d["sort"] == "updated"), null) + .Returns(Task.Factory.StartNew>>(() => firstPageResponse)); + gitHubClient.Connection.GetAsync>(secondPageUrl, null, null) + .Returns(Task.Factory.StartNew>>(() => secondPageResponse)); + gitHubClient.Connection.GetAsync>(thirdPageUrl, null, null) + .Returns(Task.Factory.StartNew>>(() => lastPageResponse)); + + var client = new ObservablePullRequestReviewCommentsClient(gitHubClient); + + var request = new PullRequestReviewCommentRequest + { + Direction = SortDirection.Descending, + Since = new DateTimeOffset(2013, 11, 15, 11, 43, 01, 00, new TimeSpan()), + Sort = PullRequestReviewCommentSort.Updated, + }; + + var results = await client.GetForRepository("fakeOwner", "fakeRepoName", request).ToArray(); + + Assert.Equal(8, results.Length); + gitHubClient.Connection.Received(1).GetAsync>(firstPageUrl, + Arg.Is>(d => d.Count == 3 + && d["direction"] == "desc" + && d["since"] == "2013-11-15T11:43:01Z" + && d["sort"] == "updated"), null); + gitHubClient.Connection.Received(1).GetAsync>(secondPageUrl, null, null); + gitHubClient.Connection.Received(1).GetAsync>(thirdPageUrl, null, null); + } + + [Fact] + public async Task RequestsCorrectUrlWithoutSelectedSortingArguments() + { + var firstPageUrl = new Uri("repos/fakeOwner/fakeRepoName/pulls/comments", UriKind.Relative); + var secondPageUrl = new Uri("https://example.com/page/2"); + var firstPageLinks = new Dictionary { { "next", secondPageUrl } }; + var firstPageResponse = new ApiResponse> + { + BodyAsObject = new List + { + new PullRequestReviewComment {Id = 1}, + new PullRequestReviewComment {Id = 2}, + new PullRequestReviewComment {Id = 3} + }, + ApiInfo = CreateApiInfo(firstPageLinks) + }; + var thirdPageUrl = new Uri("https://example.com/page/3"); + var secondPageLinks = new Dictionary { { "next", thirdPageUrl } }; + var secondPageResponse = new ApiResponse> + { + BodyAsObject = new List + { + new PullRequestReviewComment {Id = 4}, + new PullRequestReviewComment {Id = 5}, + new PullRequestReviewComment {Id = 6} + }, + ApiInfo = CreateApiInfo(secondPageLinks) + }; + var lastPageResponse = new ApiResponse> + { + BodyAsObject = new List + { + new PullRequestReviewComment {Id = 7}, + new PullRequestReviewComment {Id = 8}, + }, + ApiInfo = CreateApiInfo(new Dictionary()) + }; + + var gitHubClient = Substitute.For(); + + gitHubClient.Connection.GetAsync>(firstPageUrl, + Arg.Is>(d => d.Count == 2 + && d["direction"] == "asc" + && d["sort"] == "created"), null) + .Returns(Task.Factory.StartNew>>(() => firstPageResponse)); + gitHubClient.Connection.GetAsync>(secondPageUrl, null, null) + .Returns(Task.Factory.StartNew>>(() => secondPageResponse)); + gitHubClient.Connection.GetAsync>(thirdPageUrl, null, null) + .Returns(Task.Factory.StartNew>>(() => lastPageResponse)); + + var client = new ObservablePullRequestReviewCommentsClient(gitHubClient); + + var results = await client.GetForRepository("fakeOwner", "fakeRepoName").ToArray(); + + Assert.Equal(8, results.Length); + gitHubClient.Connection.Received(1).GetAsync>(firstPageUrl, + Arg.Is>(d => d.Count == 2 + && d["direction"] == "asc" + && d["sort"] == "created"), null); + gitHubClient.Connection.Received(1).GetAsync>(secondPageUrl, null, null); + gitHubClient.Connection.Received(1).GetAsync>(thirdPageUrl, null, null); + } + + [Fact] + public async Task EnsuresArgumentsNotNull() + { + var client = new ObservablePullRequestReviewCommentsClient(Substitute.For()); + + var request = new PullRequestReviewCommentRequest(); + + await AssertEx.Throws(async () => await client.GetForRepository(null, "name", request)); + await AssertEx.Throws(async () => await client.GetForRepository("", "name", request)); + await AssertEx.Throws(async () => await client.GetForRepository("owner", null, request)); + await AssertEx.Throws(async () => await client.GetForRepository("owner", "", request)); + await AssertEx.Throws(async () => await client.GetForRepository("owner", "name", null)); + } + } + + public class TheGetCommentMethod + { + [Fact] + public void GetsFromClientPullRequestComment() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewCommentsClient(gitHubClient); + + client.GetComment("fakeOwner", "fakeRepoName", 53); + + gitHubClient.PullRequest.Comment.Received().GetComment("fakeOwner", "fakeRepoName", 53); + } + + [Fact] + public async Task EnsuresArgumentsNonNull() + { + var client = new ObservablePullRequestReviewCommentsClient(Substitute.For()); + + await AssertEx.Throws(async () => await client.GetComment(null, "name", 1)); + await AssertEx.Throws(async () => await client.GetComment("", "name", 1)); + await AssertEx.Throws(async () => await client.GetComment("owner", null, 1)); + await AssertEx.Throws(async () => await client.GetComment("owner", "", 1)); + await AssertEx.Throws(async () => await client.GetComment(null, null, 1)); + await AssertEx.Throws(async () => await client.GetComment("", "", 1)); + } + } + + public class TheCreateMethod + { + [Fact] + public void PostsToCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewCommentsClient(gitHubClient); + + var comment = new PullRequestReviewCommentCreate + { + Body = "Comment content", + CommitId = "qe3dsdsf6", + Path = "file.css", + Position = 7, + }; + + client.Create("fakeOwner", "fakeRepoName", 13, comment); + + gitHubClient.PullRequest.Comment.Received().Create("fakeOwner", "fakeRepoName", 13, comment); + } + + [Fact] + public async Task EnsuresArgumentsNotNull() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewCommentsClient(gitHubClient); + + string body = "Comment content"; + string commitId = "qe3dsdsf6"; + string path = "file.css"; + int position = 7; + + var comment = new PullRequestReviewCommentCreate + { + Body = body, + CommitId = commitId, + Path = path, + Position = position, + }; + + await AssertEx.Throws(async () => await client.Create(null, "name", 1, comment)); + await AssertEx.Throws(async () => await client.Create("", "name", 1, comment)); + await AssertEx.Throws(async () => await client.Create("owner", null, 1, comment)); + await AssertEx.Throws(async () => await client.Create("owner", "", 1, comment)); + await AssertEx.Throws(async () => await client.Create("owner", "name", 1, null)); + + comment.Body = null; + await AssertEx.Throws(async () => await client.Create("owner", "name", 1, comment)); + comment.Body = ""; + await AssertEx.Throws(async () => await client.Create("owner", "name", 1, comment)); + comment.Body = body; + + comment.CommitId = null; + await AssertEx.Throws(async () => await client.Create("owner", "name", 1, comment)); + comment.CommitId = ""; + await AssertEx.Throws(async () => await client.Create("owner", "name", 1, comment)); + comment.CommitId = commitId; + + comment.Path = null; + await AssertEx.Throws(async () => await client.Create("owner", "name", 1, comment)); + comment.Path = ""; + await AssertEx.Throws(async () => await client.Create("owner", "name", 1, comment)); + comment.Path = path; + } + } + + public class TheCreateReplyMethod + { + [Fact] + public void PostsToCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewCommentsClient(gitHubClient); + + var comment = new PullRequestReviewCommentReplyCreate + { + Body = "Comment content", + InReplyTo = 9, + }; + + client.CreateReply("fakeOwner", "fakeRepoName", 13, comment); + + gitHubClient.PullRequest.Comment.Received().CreateReply("fakeOwner", "fakeRepoName", 13, comment); + } + + [Fact] + public async Task EnsuresArgumentsNotNull() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewCommentsClient(gitHubClient); + + string body = "Comment content"; + int inReplyTo = 7; + + var comment = new PullRequestReviewCommentReplyCreate + { + Body = body, + InReplyTo = inReplyTo, + }; + + await AssertEx.Throws(async () => await client.CreateReply(null, "name", 1, comment)); + await AssertEx.Throws(async () => await client.CreateReply("", "name", 1, comment)); + await AssertEx.Throws(async () => await client.CreateReply("owner", null, 1, comment)); + await AssertEx.Throws(async () => await client.CreateReply("owner", "", 1, comment)); + await AssertEx.Throws(async () => await client.CreateReply("owner", "name", 1, null)); + + comment.Body = null; + await AssertEx.Throws(async () => await client.CreateReply("owner", "name", 1, comment)); + comment.Body = ""; + await AssertEx.Throws(async () => await client.CreateReply("owner", "name", 1, comment)); + comment.Body = body; + } + } + + public class TheEditMethod + { + [Fact] + public void PostsToCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewCommentsClient(gitHubClient); + + var comment = new PullRequestReviewCommentEdit + { + Body = "New comment content", + }; + + client.Edit("fakeOwner", "fakeRepoName", 13, comment); + + gitHubClient.PullRequest.Comment.Received().Edit("fakeOwner", "fakeRepoName", 13, comment); + } + + [Fact] + public async Task EnsuresArgumentsNotNull() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewCommentsClient(gitHubClient); + + var body = "New comment content"; + + var comment = new PullRequestReviewCommentEdit + { + Body = body, + }; + + await AssertEx.Throws(async () => await client.Edit(null, "name", 1, comment)); + await AssertEx.Throws(async () => await client.Edit("", "name", 1, comment)); + await AssertEx.Throws(async () => await client.Edit("owner", null, 1, comment)); + await AssertEx.Throws(async () => await client.Edit("owner", "", 1, comment)); + await AssertEx.Throws(async () => await client.Edit("owner", "name", 1, null)); + + comment.Body = null; + await AssertEx.Throws(async () => await client.Edit("owner", "name", 1, comment)); + comment.Body = ""; + await AssertEx.Throws(async () => await client.Edit("owner", "name", 1, comment)); + comment.Body = body; + } + } + + public class TheDeleteMethod + { + [Fact] + public void PostsToCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewCommentsClient(gitHubClient); + + client.Delete("fakeOwner", "fakeRepoName", 13); + + gitHubClient.PullRequest.Comment.Received().Delete("fakeOwner", "fakeRepoName", 13); + } + + [Fact] + public async Task EnsuresArgumentsNotNull() + { + var gitHubClient = Substitute.For(); + var client = new ObservablePullRequestReviewCommentsClient(gitHubClient); + + await AssertEx.Throws(async () => await client.Delete(null, "name", 1)); + await AssertEx.Throws(async () => await client.Delete("", "name", 1)); + await AssertEx.Throws(async () => await client.Delete("owner", null, 1)); + await AssertEx.Throws(async () => await client.Delete("owner", "", 1)); + } + } + } +} diff --git a/Octokit/Clients/IPullRequestReviewCommentsClient.cs b/Octokit/Clients/IPullRequestReviewCommentsClient.cs new file mode 100644 index 00000000..37cd15b4 --- /dev/null +++ b/Octokit/Clients/IPullRequestReviewCommentsClient.cs @@ -0,0 +1,90 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Octokit +{ + public interface IPullRequestReviewCommentsClient + { + /// + /// Gets review comments for a specified pull request. + /// + /// http://developer.github.com/v3/pulls/comments/#list-comments-on-a-pull-request + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The list of s for the specified pull request + Task> GetForPullRequest(string owner, string name, int number); + + /// + /// Gets a list of the pull request review comments in a specified repository. + /// + /// http://developer.github.com/v3/pulls/comments/#list-comments-in-a-repository + /// The owner of the repository + /// The name of the repository + /// The list of s for the specified repository + Task> GetForRepository(string owner, string name); + + /// + /// Gets a list of the pull request review comments in a specified repository. + /// + /// http://developer.github.com/v3/pulls/comments/#list-comments-in-a-repository + /// The owner of the repository + /// The name of the repository + /// The sorting parameters + /// The list of s for the specified repository + Task> GetForRepository(string owner, string name, PullRequestReviewCommentRequest request); + + /// + /// Gets a single pull request review comment by number. + /// + /// http://developer.github.com/v3/pulls/comments/#get-a-single-comment + /// The owner of the repository + /// The name of the repository + /// The pull request review comment number + /// The + Task GetComment(string owner, string name, int number); + + /// + /// Creates a comment on a pull request review. + /// + /// http://developer.github.com/v3/pulls/comments/#create-a-comment + /// The owner of the repository + /// The name of the repository + /// The Pull Request number + /// The comment + /// The created + Task Create(string owner, string name, int number, PullRequestReviewCommentCreate comment); + + /// + /// Creates a comment on a pull request review as a reply to another comment. + /// + /// http://developer.github.com/v3/pulls/comments/#create-a-comment + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The comment + /// The created + Task CreateReply(string owner, string name, int number, PullRequestReviewCommentReplyCreate comment); + + /// + /// Edits a comment on a pull request review. + /// + /// http://developer.github.com/v3/pulls/comments/#edit-a-comment + /// The owner of the repository + /// The name of the repository + /// The pull request review comment number + /// The edited comment + /// The edited + Task Edit(string owner, string name, int number, PullRequestReviewCommentEdit comment); + + /// + /// Deletes a comment on a pull request review. + /// + /// http://developer.github.com/v3/pulls/comments/#delete-a-comment + /// The owner of the repository + /// The name of the repository + /// The pull request review comment number + /// + Task Delete(string owner, string name, int number); + } +} diff --git a/Octokit/Clients/IPullRequestsClient.cs b/Octokit/Clients/IPullRequestsClient.cs new file mode 100644 index 00000000..1870c65b --- /dev/null +++ b/Octokit/Clients/IPullRequestsClient.cs @@ -0,0 +1,11 @@ + +namespace Octokit +{ + public interface IPullRequestsClient + { + /// + /// Client for managing comments. + /// + IPullRequestReviewCommentsClient Comment { get; } + } +} diff --git a/Octokit/Clients/PullRequestReviewCommentsClient.cs b/Octokit/Clients/PullRequestReviewCommentsClient.cs new file mode 100644 index 00000000..6c049b19 --- /dev/null +++ b/Octokit/Clients/PullRequestReviewCommentsClient.cs @@ -0,0 +1,149 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Octokit +{ + public class PullRequestReviewCommentsClient : ApiClient, IPullRequestReviewCommentsClient + { + public PullRequestReviewCommentsClient(IApiConnection apiConnection) + : base(apiConnection) + { + } + + /// + /// Gets review comments for a specified pull request. + /// + /// http://developer.github.com/v3/pulls/comments/#list-comments-on-a-pull-request + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The list of s for the specified pull request + public Task> GetForPullRequest(string owner, string name, int number) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return ApiConnection.GetAll(ApiUrls.PullRequestReviewComments(owner, name, number)); + } + + /// + /// Gets a list of the pull request review comments in a specified repository. + /// + /// http://developer.github.com/v3/pulls/comments/#list-comments-in-a-repository + /// The owner of the repository + /// The name of the repository + /// The list of s for the specified repository + public Task> GetForRepository(string owner, string name) + { + return GetForRepository(owner, name, new PullRequestReviewCommentRequest()); + } + + /// + /// Gets a list of the pull request review comments in a specified repository. + /// + /// http://developer.github.com/v3/pulls/comments/#list-comments-in-a-repository + /// The owner of the repository + /// The name of the repository + /// The sorting parameters + /// The list of s for the specified repository + public Task> GetForRepository(string owner, string name, PullRequestReviewCommentRequest request) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(request, "request"); + + return ApiConnection.GetAll(ApiUrls.PullRequestReviewCommentsRepository(owner, name), request.ToParametersDictionary()); + } + + /// + /// Gets a single pull request review comment by number. + /// + /// http://developer.github.com/v3/pulls/comments/#get-a-single-comment + /// The owner of the repository + /// The name of the repository + /// The pull request review comment number + /// The + public Task GetComment(string owner, string name, int number) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return ApiConnection.Get(ApiUrls.PullRequestReviewComment(owner, name, number)); + } + + /// + /// Creates a comment on a pull request review. + /// + /// http://developer.github.com/v3/pulls/comments/#create-a-comment + /// The owner of the repository + /// The name of the repository + /// The Pull Request number + /// The comment + /// The created + public Task Create(string owner, string name, int number, PullRequestReviewCommentCreate comment) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(comment, "comment"); + Ensure.ArgumentNotNullOrEmptyString(comment.Body, "body"); + Ensure.ArgumentNotNullOrEmptyString(comment.CommitId, "commitId"); + Ensure.ArgumentNotNullOrEmptyString(comment.Path, "path"); + + return ApiConnection.Post(ApiUrls.PullRequestReviewComments(owner, name, number), comment); + } + + /// + /// Creates a comment on a pull request review as a reply to another comment. + /// + /// http://developer.github.com/v3/pulls/comments/#create-a-comment + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The comment + /// The created + public Task CreateReply(string owner, string name, int number, PullRequestReviewCommentReplyCreate comment) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(comment, "comment"); + Ensure.ArgumentNotNullOrEmptyString(comment.Body, "body"); + + return ApiConnection.Post(ApiUrls.PullRequestReviewComments(owner, name, number), comment); + } + + /// + /// Edits a comment on a pull request review. + /// + /// http://developer.github.com/v3/pulls/comments/#edit-a-comment + /// The owner of the repository + /// The name of the repository + /// The pull request review comment number + /// The edited comment + /// The edited + public Task Edit(string owner, string name, int number, PullRequestReviewCommentEdit comment) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(comment, "comment"); + Ensure.ArgumentNotNullOrEmptyString(comment.Body, "body"); + + return ApiConnection.Patch(ApiUrls.PullRequestReviewComment(owner, name, number), comment); + } + + /// + /// Deletes a comment on a pull request review. + /// + /// http://developer.github.com/v3/pulls/comments/#delete-a-comment + /// The owner of the repository + /// The name of the repository + /// The pull request review comment number + /// + public Task Delete(string owner, string name, int number) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return ApiConnection.Delete(ApiUrls.PullRequestReviewComment(owner, name, number)); + } + } +} diff --git a/Octokit/Clients/PullRequestsClient.cs b/Octokit/Clients/PullRequestsClient.cs new file mode 100644 index 00000000..6e37d11c --- /dev/null +++ b/Octokit/Clients/PullRequestsClient.cs @@ -0,0 +1,16 @@ + +namespace Octokit +{ + public class PullRequestsClient : ApiClient, IPullRequestsClient + { + public PullRequestsClient(IApiConnection apiConnection) : base(apiConnection) + { + Comment = new PullRequestReviewCommentsClient(apiConnection); + } + + /// + /// Client for managing comments. + /// + public IPullRequestReviewCommentsClient Comment { get; private set; } + } +} diff --git a/Octokit/GitHubClient.cs b/Octokit/GitHubClient.cs index 9bbcd384..275d4074 100644 --- a/Octokit/GitHubClient.cs +++ b/Octokit/GitHubClient.cs @@ -85,6 +85,7 @@ namespace Octokit Miscellaneous = new MiscellaneousClient(connection); Notification = new NotificationsClient(apiConnection); Organization = new OrganizationsClient(apiConnection); + PullRequest = new PullRequestsClient(apiConnection); Repository = new RepositoriesClient(apiConnection); Release = new ReleasesClient(apiConnection); User = new UsersClient(apiConnection); @@ -131,6 +132,7 @@ namespace Octokit public IIssuesClient Issue { get; private set; } public IMiscellaneousClient Miscellaneous { get; private set; } public IOrganizationsClient Organization { get; private set; } + public IPullRequestsClient PullRequest { get; private set; } public IRepositoriesClient Repository { get; private set; } public IReleasesClient Release { get; private set; } public ISshKeysClient SshKey { get; private set; } diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index be8ebd59..dbe30dfb 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -548,5 +548,40 @@ namespace Octokit { return "users/{0}/events/orgs/{1}".FormatUri(user, organization); } + + /// + /// Returns the for the comments of a specified pull request review. + /// + /// The owner of the repository + /// The name of the repository + /// The pull request number + /// The + public static Uri PullRequestReviewComments(string owner, string name, int number) + { + return "repos/{0}/{1}/pulls/{2}/comments".FormatUri(owner, name, number); + } + + /// + /// Returns the for the specified pull request review comment. + /// + /// The owner of the repository + /// The name of the repository + /// The comment number + /// The + 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 pull request review comments on a specified repository. + /// + /// The owner of the repository + /// The name of the repository + /// The + public static Uri PullRequestReviewCommentsRepository(string owner, string name) + { + return "repos/{0}/{1}/pulls/comments".FormatUri(owner, name); + } } } diff --git a/Octokit/IGitHubClient.cs b/Octokit/IGitHubClient.cs index c8586208..2e6b8ec0 100644 --- a/Octokit/IGitHubClient.cs +++ b/Octokit/IGitHubClient.cs @@ -11,6 +11,7 @@ namespace Octokit IIssuesClient Issue { get; } IMiscellaneousClient Miscellaneous { get; } IOrganizationsClient Organization { get; } + IPullRequestsClient PullRequest { get; } IRepositoriesClient Repository { get; } IReleasesClient Release { get; } ISshKeysClient SshKey { get; } diff --git a/Octokit/Models/Request/PullRequestReviewCommentCreate.cs b/Octokit/Models/Request/PullRequestReviewCommentCreate.cs new file mode 100644 index 00000000..08e9e581 --- /dev/null +++ b/Octokit/Models/Request/PullRequestReviewCommentCreate.cs @@ -0,0 +1,28 @@ +using Octokit.Internal; + +namespace Octokit +{ + public class PullRequestReviewCommentCreate : RequestParameters + { + /// + /// The text of the comment. + /// + public string Body { get; set; } + + /// + /// The SHA of the commit to comment on. + /// + [Parameter(Key = "commit_id")] + public string CommitId { get; set; } + + /// + /// The relative path of the file to comment on. + /// + public string Path { get; set; } + + /// + /// The line index in the diff to comment on. + /// + public int Position { get; set; } + } +} diff --git a/Octokit/Models/Request/PullRequestReviewCommentEdit.cs b/Octokit/Models/Request/PullRequestReviewCommentEdit.cs new file mode 100644 index 00000000..1ad6f0ba --- /dev/null +++ b/Octokit/Models/Request/PullRequestReviewCommentEdit.cs @@ -0,0 +1,11 @@ + +namespace Octokit +{ + public class PullRequestReviewCommentEdit : RequestParameters + { + /// + /// The text of the comment. + /// + public string Body { get; set; } + } +} diff --git a/Octokit/Models/Request/PullRequestReviewCommentReplyCreate.cs b/Octokit/Models/Request/PullRequestReviewCommentReplyCreate.cs new file mode 100644 index 00000000..a8e23599 --- /dev/null +++ b/Octokit/Models/Request/PullRequestReviewCommentReplyCreate.cs @@ -0,0 +1,18 @@ +using Octokit.Internal; + +namespace Octokit +{ + public class PullRequestReviewCommentReplyCreate : RequestParameters + { + /// + /// The text of the comment. + /// + public string Body { get; set; } + + /// + /// The comment Id to reply to. + /// + [Parameter(Key = "in_reply_to")] + public int InReplyTo { get; set; } + } +} diff --git a/Octokit/Models/Request/PullRequestReviewCommentRequest.cs b/Octokit/Models/Request/PullRequestReviewCommentRequest.cs new file mode 100644 index 00000000..125f8059 --- /dev/null +++ b/Octokit/Models/Request/PullRequestReviewCommentRequest.cs @@ -0,0 +1,31 @@ +using System; + +namespace Octokit +{ + public class PullRequestReviewCommentRequest : RequestParameters + { + public PullRequestReviewCommentRequest() + { + // Default arguments + + Sort = PullRequestReviewCommentSort.Created; + Direction = SortDirection.Ascending; + Since = null; + } + + /// + /// Can be either created or updated. Default: created. + /// + public PullRequestReviewCommentSort Sort { get; set; } + + /// + /// Can be either asc or desc. Default: asc. + /// + public SortDirection Direction { get; set; } + + /// + /// Only comments updated at or after this time are returned. This is a timestamp in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ. + /// + public DateTimeOffset? Since { get; set; } + } +} diff --git a/Octokit/Models/Response/PullRequestReviewComment.cs b/Octokit/Models/Response/PullRequestReviewComment.cs new file mode 100644 index 00000000..734be9f2 --- /dev/null +++ b/Octokit/Models/Response/PullRequestReviewComment.cs @@ -0,0 +1,118 @@ +using System; + +namespace Octokit +{ + public class PullRequestReviewComment + { + /// + /// URL of the comment via the API. + /// + public Uri Url { get; set; } + + /// + /// The comment Id. + /// + public int Id { get; set; } + + /// + /// The diff hunk the comment is about. + /// + public string DiffHunk { get; set; } + + /// + /// The relative path of the file the comment is about. + /// + public string Path { get; set; } + + /// + /// The line index in the diff. + /// + public int? Position { get; set; } + + /// + /// The comment original position. + /// + public int? OriginalPosition { get; set; } + + /// + /// The commit Id the comment is associated with. + /// + public string CommitId { get; set; } + + /// + /// The original commit Id the comment is associated with. + /// + public string OriginalCommitId { get; set; } + + /// + /// The user that created the comment. + /// + public User User { get; set; } + + /// + /// The text of the comment. + /// + public string Body { get; set; } + + /// + /// The date the comment was created. + /// + public DateTimeOffset CreatedAt { get; set; } + + /// + /// The date the comment was last updated. + /// + public DateTimeOffset UpdatedAt { get; set; } + + /// + /// The URL for this comment on Github.com + /// + public Uri HtmlUrl { get; set; } + + /// + /// The URL for the pull request via the API. + /// + public Uri PullRequestUrl { get; set; } + + /// + /// Contains Url, HtmlUrl and PullRequestUrl + /// + public PullRequestReviewCommentLinks Links { get; set; } + } + + public class PullRequestReviewCommentLinks + { + /// + /// URL of the comment via the API. + /// + public PullRequestReviewCommentLink Self { get; set; } + + /// + /// The URL for this comment on Github.com + /// + public PullRequestReviewCommentLink Html { get; set; } + + /// + /// The URL for the pull request via the API. + /// + public PullRequestReviewCommentLink PullRequest { get; set; } + } + + public class PullRequestReviewCommentLink + { + public Uri Href { get; set; } + } + + public enum PullRequestReviewCommentSort + { + /// + /// Sort by create date (default) + /// + Created, + + /// + /// Sort by the date of the last update + /// + Updated, + } +} diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index b6d6d656..0d1ca16b 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -48,6 +48,14 @@ + + + + + + + + @@ -104,6 +112,7 @@ + diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index 495049cd..1e97295a 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -58,6 +58,14 @@ + + + + + + + + @@ -103,6 +111,7 @@ + diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index eb9acc49..400e98aa 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -53,6 +53,14 @@ + + + + + + + + @@ -98,6 +106,7 @@ + diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index fdd69ff7..db97e0ee 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -58,6 +58,10 @@ + + + + @@ -200,6 +204,11 @@ + + + + + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index b457bda0..f981c584 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -55,6 +55,14 @@ + + + + + + + + @@ -95,6 +103,7 @@ + @@ -237,4 +246,4 @@ --> - + \ No newline at end of file