Implement Draft Pull Requests (#2009)

* Add draft PR preview header

* Add Draft property to models

* Update pull requests client and tests to use draft PR accept header

* Update observable pull requests client and tests to use draft PR accept header

* Add integration tests to create and retrieve draft pull requests
This commit is contained in:
Henrik Andersson
2019-09-23 02:38:56 +10:00
committed by Brendan Forster
parent d955d7facd
commit fafbf33b78
8 changed files with 106 additions and 39 deletions
@@ -111,7 +111,7 @@ namespace Octokit.Reactive
Ensure.ArgumentNotNullOrEmptyString(name, nameof(name));
Ensure.ArgumentNotNull(options, nameof(options));
return _connection.GetAndFlattenAllPages<PullRequest>(ApiUrls.PullRequests(owner, name), options);
return _connection.GetAndFlattenAllPages<PullRequest>(ApiUrls.PullRequests(owner, name), null, AcceptHeaders.DraftPullRequestApiPreview, options);
}
/// <summary>
@@ -126,7 +126,7 @@ namespace Octokit.Reactive
{
Ensure.ArgumentNotNull(options, nameof(options));
return _connection.GetAndFlattenAllPages<PullRequest>(ApiUrls.PullRequests(repositoryId), options);
return _connection.GetAndFlattenAllPages<PullRequest>(ApiUrls.PullRequests(repositoryId), null, AcceptHeaders.DraftPullRequestApiPreview, options);
}
/// <summary>
@@ -180,7 +180,7 @@ namespace Octokit.Reactive
Ensure.ArgumentNotNull(options, nameof(options));
return _connection.GetAndFlattenAllPages<PullRequest>(ApiUrls.PullRequests(owner, name),
request.ToParametersDictionary(), options);
request.ToParametersDictionary(), AcceptHeaders.DraftPullRequestApiPreview, options);
}
/// <summary>
@@ -198,7 +198,7 @@ namespace Octokit.Reactive
Ensure.ArgumentNotNull(options, nameof(options));
return _connection.GetAndFlattenAllPages<PullRequest>(ApiUrls.PullRequests(repositoryId),
request.ToParametersDictionary(), options);
request.ToParametersDictionary(), AcceptHeaders.DraftPullRequestApiPreview, options);
}
/// <summary>
@@ -38,6 +38,17 @@ public class PullRequestsClientTests : IDisposable
Assert.Equal("a pull request", result.Title);
}
[IntegrationTest]
public async Task CanCreateDraft()
{
await CreateTheWorld();
var newPullRequest = new NewPullRequest("a draft pull request", branchName, "master") { Draft = true };
var result = await _fixture.Create(Helper.UserName, _context.RepositoryName, newPullRequest);
Assert.Equal("a draft pull request", result.Title);
Assert.True(result.Draft);
}
[IntegrationTest]
public async Task CanCreateWithRepositoryId()
{
@@ -48,6 +59,17 @@ public class PullRequestsClientTests : IDisposable
Assert.Equal("a pull request", result.Title);
}
[IntegrationTest]
public async Task CanCreateDraftWithRepositoryId()
{
await CreateTheWorld();
var newPullRequest = new NewPullRequest("a draft pull request", branchName, "master") { Draft = true };
var result = await _fixture.Create(_context.Repository.Id, newPullRequest);
Assert.Equal("a draft pull request", result.Title);
Assert.True(result.Draft);
}
[IntegrationTest]
public async Task CanGetForRepository()
{
@@ -63,6 +85,22 @@ public class PullRequestsClientTests : IDisposable
Assert.True(pullRequests[0].Id > 0);
}
[IntegrationTest]
public async Task CanGetDraftForRepository()
{
await CreateTheWorld();
var newPullRequest = new NewPullRequest("a draft pull request", branchName, "master") { Draft = true };
var result = await _fixture.Create(Helper.UserName, _context.RepositoryName, newPullRequest);
var pullRequests = await _fixture.GetAllForRepository(Helper.UserName, _context.RepositoryName);
Assert.Equal(1, pullRequests.Count);
Assert.Equal(result.Title, pullRequests[0].Title);
Assert.Equal(result.Draft, pullRequests[0].Draft);
Assert.True(pullRequests[0].Id > 0);
}
[IntegrationTest]
public async Task CanGetForRepositoryWithRepositoryId()
{
@@ -77,6 +115,22 @@ public class PullRequestsClientTests : IDisposable
Assert.Equal(result.Title, pullRequests[0].Title);
}
[IntegrationTest]
public async Task CanGetDraftForRepositoryWithRepositoryId()
{
await CreateTheWorld();
var newPullRequest = new NewPullRequest("a draft pull request", branchName, "master") { Draft = true };
var result = await _fixture.Create(_context.Repository.Id, newPullRequest);
var pullRequests = await _fixture.GetAllForRepository(_context.Repository.Id);
Assert.Equal(1, pullRequests.Count);
Assert.Equal(result.Title, pullRequests[0].Title);
Assert.Equal(result.Draft, pullRequests[0].Draft);
Assert.True(pullRequests[0].Id > 0);
}
[IntegrationTest]
public async Task CanGetWithAssigneesForRepository()
{
@@ -18,7 +18,7 @@ namespace Octokit.Tests.Clients
await client.Get("fake", "repo", 42);
connection.Received().Get<PullRequest>(Arg.Is<Uri>(u => u.ToString() == "repos/fake/repo/pulls/42"));
connection.Received().Get<PullRequest>(Arg.Is<Uri>(u => u.ToString() == "repos/fake/repo/pulls/42"), Arg.Any<Dictionary<string, string>>(), "application/vnd.github.shadow-cat-preview+json");
}
[Fact]
@@ -29,7 +29,7 @@ namespace Octokit.Tests.Clients
await client.Get(1, 42);
connection.Received().Get<PullRequest>(Arg.Is<Uri>(u => u.ToString() == "repositories/1/pulls/42"));
connection.Received().Get<PullRequest>(Arg.Is<Uri>(u => u.ToString() == "repositories/1/pulls/42"), Arg.Any<Dictionary<string, string>>(), "application/vnd.github.shadow-cat-preview+json");
}
[Fact]
@@ -56,7 +56,7 @@ namespace Octokit.Tests.Clients
await client.GetAllForRepository("fake", "repo");
connection.Received().GetAll<PullRequest>(Arg.Is<Uri>(u => u.ToString() == "repos/fake/repo/pulls"),
Arg.Any<Dictionary<string, string>>(), Args.ApiOptions);
Arg.Any<Dictionary<string, string>>(), "application/vnd.github.shadow-cat-preview+json", Args.ApiOptions);
}
[Fact]
@@ -68,7 +68,7 @@ namespace Octokit.Tests.Clients
await client.GetAllForRepository(1);
connection.Received().GetAll<PullRequest>(Arg.Is<Uri>(u => u.ToString() == "repositories/1/pulls"),
Arg.Any<Dictionary<string, string>>(), Args.ApiOptions);
Arg.Any<Dictionary<string, string>>(), "application/vnd.github.shadow-cat-preview+json", Args.ApiOptions);
}
[Fact]
@@ -87,7 +87,7 @@ namespace Octokit.Tests.Clients
await client.GetAllForRepository("fake", "repo", options);
connection.Received().GetAll<PullRequest>(Arg.Is<Uri>(u => u.ToString() == "repos/fake/repo/pulls"),
Arg.Any<Dictionary<string, string>>(), options);
Arg.Any<Dictionary<string, string>>(), "application/vnd.github.shadow-cat-preview+json", options);
}
[Fact]
@@ -106,7 +106,7 @@ namespace Octokit.Tests.Clients
await client.GetAllForRepository(1, options);
connection.Received().GetAll<PullRequest>(Arg.Is<Uri>(u => u.ToString() == "repositories/1/pulls"),
Arg.Any<Dictionary<string, string>>(), options);
Arg.Any<Dictionary<string, string>>(), "application/vnd.github.shadow-cat-preview+json", options);
}
[Fact]
@@ -123,7 +123,7 @@ namespace Octokit.Tests.Clients
&& d["state"] == "open"
&& d["base"] == "fake_base_branch"
&& d["sort"] == "created"
&& d["direction"] == "desc"), Args.ApiOptions);
&& d["direction"] == "desc"), "application/vnd.github.shadow-cat-preview+json", Args.ApiOptions);
}
[Fact]
@@ -140,7 +140,7 @@ namespace Octokit.Tests.Clients
&& d["state"] == "open"
&& d["base"] == "fake_base_branch"
&& d["sort"] == "created"
&& d["direction"] == "desc"), Args.ApiOptions);
&& d["direction"] == "desc"), "application/vnd.github.shadow-cat-preview+json", Args.ApiOptions);
}
[Fact]
@@ -164,7 +164,7 @@ namespace Octokit.Tests.Clients
&& d["state"] == "open"
&& d["base"] == "fake_base_branch"
&& d["sort"] == "created"
&& d["direction"] == "desc"), options);
&& d["direction"] == "desc"), "application/vnd.github.shadow-cat-preview+json", options);
}
[Fact]
@@ -188,7 +188,7 @@ namespace Octokit.Tests.Clients
&& d["state"] == "open"
&& d["base"] == "fake_base_branch"
&& d["sort"] == "created"
&& d["direction"] == "desc"), options);
&& d["direction"] == "desc"), "application/vnd.github.shadow-cat-preview+json", options);
}
[Fact]
@@ -243,7 +243,7 @@ namespace Octokit.Tests.Clients
await client.Create("fake", "repo", newPullRequest);
connection.Received().Post<PullRequest>(Arg.Is<Uri>(u => u.ToString() == "repos/fake/repo/pulls"),
newPullRequest);
newPullRequest, "application/vnd.github.shadow-cat-preview+json");
}
[Fact]
@@ -256,7 +256,7 @@ namespace Octokit.Tests.Clients
await client.Create(1, newPullRequest);
connection.Received().Post<PullRequest>(Arg.Is<Uri>(u => u.ToString() == "repositories/1/pulls"),
newPullRequest);
newPullRequest, "application/vnd.github.shadow-cat-preview+json");
}
[Fact]
@@ -288,7 +288,7 @@ namespace Octokit.Tests.Clients
await client.Update("fake", "repo", 42, pullRequestUpdate);
connection.Received().Patch<PullRequest>(Arg.Is<Uri>(u => u.ToString() == "repos/fake/repo/pulls/42"),
pullRequestUpdate);
pullRequestUpdate, "application/vnd.github.shadow-cat-preview+json");
}
[Fact]
@@ -301,7 +301,7 @@ namespace Octokit.Tests.Clients
await client.Update(1, 42, pullRequestUpdate);
connection.Received().Patch<PullRequest>(Arg.Is<Uri>(u => u.ToString() == "repositories/1/pulls/42"),
pullRequestUpdate);
pullRequestUpdate, "application/vnd.github.shadow-cat-preview+json");
}
[Fact]
@@ -209,11 +209,11 @@ namespace Octokit.Tests.Reactive
}
);
var gitHubClient = Substitute.For<IGitHubClient>();
gitHubClient.Connection.Get<List<PullRequest>>(firstPageUrl, Args.EmptyDictionary, null)
gitHubClient.Connection.Get<List<PullRequest>>(firstPageUrl, Args.EmptyDictionary, "application/vnd.github.shadow-cat-preview+json")
.Returns(Task.Factory.StartNew<IApiResponse<List<PullRequest>>>(() => firstPageResponse));
gitHubClient.Connection.Get<List<PullRequest>>(secondPageUrl, Args.EmptyDictionary, null)
gitHubClient.Connection.Get<List<PullRequest>>(secondPageUrl, Args.EmptyDictionary, "application/vnd.github.shadow-cat-preview+json")
.Returns(Task.Factory.StartNew<IApiResponse<List<PullRequest>>>(() => secondPageResponse));
gitHubClient.Connection.Get<List<PullRequest>>(thirdPageUrl, Args.EmptyDictionary, null)
gitHubClient.Connection.Get<List<PullRequest>>(thirdPageUrl, Args.EmptyDictionary, "application/vnd.github.shadow-cat-preview+json")
.Returns(Task.Factory.StartNew<IApiResponse<List<PullRequest>>>(() => lastPageResponse));
var client = new ObservablePullRequestsClient(gitHubClient);
@@ -262,11 +262,11 @@ namespace Octokit.Tests.Reactive
}
);
var gitHubClient = Substitute.For<IGitHubClient>();
gitHubClient.Connection.Get<List<PullRequest>>(firstPageUrl, Args.EmptyDictionary, null)
gitHubClient.Connection.Get<List<PullRequest>>(firstPageUrl, Args.EmptyDictionary, "application/vnd.github.shadow-cat-preview+json")
.Returns(Task.Factory.StartNew<IApiResponse<List<PullRequest>>>(() => firstPageResponse));
gitHubClient.Connection.Get<List<PullRequest>>(secondPageUrl, Args.EmptyDictionary, null)
gitHubClient.Connection.Get<List<PullRequest>>(secondPageUrl, Args.EmptyDictionary, "application/vnd.github.shadow-cat-preview+json")
.Returns(Task.Factory.StartNew<IApiResponse<List<PullRequest>>>(() => secondPageResponse));
gitHubClient.Connection.Get<List<PullRequest>>(thirdPageUrl, Args.EmptyDictionary, null)
gitHubClient.Connection.Get<List<PullRequest>>(thirdPageUrl, Args.EmptyDictionary, "application/vnd.github.shadow-cat-preview+json")
.Returns(Task.Factory.StartNew<IApiResponse<List<PullRequest>>>(() => lastPageResponse));
var client = new ObservablePullRequestsClient(gitHubClient);
@@ -321,21 +321,21 @@ namespace Octokit.Tests.Reactive
&& d["state"] == "open"
&& d["base"] == "fake_base_branch"
&& d["sort"] == "created"
&& d["direction"] == "desc"), Arg.Any<string>())
&& d["direction"] == "desc"), "application/vnd.github.shadow-cat-preview+json")
.Returns(Task.Factory.StartNew<IApiResponse<List<PullRequest>>>(() => firstPageResponse));
gitHubClient.Connection.Get<List<PullRequest>>(secondPageUrl, Arg.Is<Dictionary<string, string>>(d => d.Count == 5
&& d["head"] == "user:ref-name"
&& d["state"] == "open"
&& d["base"] == "fake_base_branch"
&& d["sort"] == "created"
&& d["direction"] == "desc"), null)
&& d["direction"] == "desc"), "application/vnd.github.shadow-cat-preview+json")
.Returns(Task.Factory.StartNew<IApiResponse<List<PullRequest>>>(() => secondPageResponse));
gitHubClient.Connection.Get<List<PullRequest>>(thirdPageUrl, Arg.Is<Dictionary<string, string>>(d => d.Count == 5
&& d["head"] == "user:ref-name"
&& d["state"] == "open"
&& d["base"] == "fake_base_branch"
&& d["sort"] == "created"
&& d["direction"] == "desc"), null)
&& d["direction"] == "desc"), "application/vnd.github.shadow-cat-preview+json")
.Returns(Task.Factory.StartNew<IApiResponse<List<PullRequest>>>(() => lastPageResponse));
var client = new ObservablePullRequestsClient(gitHubClient);
@@ -390,21 +390,21 @@ namespace Octokit.Tests.Reactive
&& d["state"] == "open"
&& d["base"] == "fake_base_branch"
&& d["sort"] == "created"
&& d["direction"] == "desc"), Arg.Any<string>())
&& d["direction"] == "desc"), "application/vnd.github.shadow-cat-preview+json")
.Returns(Task.Factory.StartNew<IApiResponse<List<PullRequest>>>(() => firstPageResponse));
gitHubClient.Connection.Get<List<PullRequest>>(secondPageUrl, Arg.Is<Dictionary<string, string>>(d => d.Count == 5
&& d["head"] == "user:ref-name"
&& d["state"] == "open"
&& d["base"] == "fake_base_branch"
&& d["sort"] == "created"
&& d["direction"] == "desc"), null)
&& d["direction"] == "desc"), "application/vnd.github.shadow-cat-preview+json")
.Returns(Task.Factory.StartNew<IApiResponse<List<PullRequest>>>(() => secondPageResponse));
gitHubClient.Connection.Get<List<PullRequest>>(thirdPageUrl, Arg.Is<Dictionary<string, string>>(d => d.Count == 5
&& d["head"] == "user:ref-name"
&& d["state"] == "open"
&& d["base"] == "fake_base_branch"
&& d["sort"] == "created"
&& d["direction"] == "desc"), null)
&& d["direction"] == "desc"), "application/vnd.github.shadow-cat-preview+json")
.Returns(Task.Factory.StartNew<IApiResponse<List<PullRequest>>>(() => lastPageResponse));
var client = new ObservablePullRequestsClient(gitHubClient);
+8 -8
View File
@@ -46,7 +46,7 @@ namespace Octokit
Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner));
Ensure.ArgumentNotNullOrEmptyString(name, nameof(name));
return ApiConnection.Get<PullRequest>(ApiUrls.PullRequest(owner, name, number));
return ApiConnection.Get<PullRequest>(ApiUrls.PullRequest(owner, name, number), null,AcceptHeaders.DraftPullRequestApiPreview);
}
/// <summary>
@@ -57,7 +57,7 @@ namespace Octokit
/// </remarks>
public Task<PullRequest> Get(long repositoryId, int number)
{
return ApiConnection.Get<PullRequest>(ApiUrls.PullRequest(repositoryId, number));
return ApiConnection.Get<PullRequest>(ApiUrls.PullRequest(repositoryId, number), null, AcceptHeaders.DraftPullRequestApiPreview);
}
/// <summary>
@@ -172,7 +172,7 @@ namespace Octokit
Ensure.ArgumentNotNull(options, nameof(options));
return ApiConnection.GetAll<PullRequest>(ApiUrls.PullRequests(owner, name),
request.ToParametersDictionary(), options);
request.ToParametersDictionary(), AcceptHeaders.DraftPullRequestApiPreview, options);
}
/// <summary>
@@ -190,7 +190,7 @@ namespace Octokit
Ensure.ArgumentNotNull(options, nameof(options));
return ApiConnection.GetAll<PullRequest>(ApiUrls.PullRequests(repositoryId),
request.ToParametersDictionary(), options);
request.ToParametersDictionary(), AcceptHeaders.DraftPullRequestApiPreview, options);
}
/// <summary>
@@ -206,7 +206,7 @@ namespace Octokit
Ensure.ArgumentNotNullOrEmptyString(name, nameof(name));
Ensure.ArgumentNotNull(newPullRequest, nameof(newPullRequest));
return ApiConnection.Post<PullRequest>(ApiUrls.PullRequests(owner, name), newPullRequest);
return ApiConnection.Post<PullRequest>(ApiUrls.PullRequests(owner, name), newPullRequest, AcceptHeaders.DraftPullRequestApiPreview);
}
/// <summary>
@@ -219,7 +219,7 @@ namespace Octokit
{
Ensure.ArgumentNotNull(newPullRequest, nameof(newPullRequest));
return ApiConnection.Post<PullRequest>(ApiUrls.PullRequests(repositoryId), newPullRequest);
return ApiConnection.Post<PullRequest>(ApiUrls.PullRequests(repositoryId), newPullRequest, AcceptHeaders.DraftPullRequestApiPreview);
}
/// <summary>
@@ -237,7 +237,7 @@ namespace Octokit
Ensure.ArgumentNotNullOrEmptyString(name, nameof(name));
Ensure.ArgumentNotNull(pullRequestUpdate, nameof(pullRequestUpdate));
return ApiConnection.Patch<PullRequest>(ApiUrls.PullRequest(owner, name, number), pullRequestUpdate);
return ApiConnection.Patch<PullRequest>(ApiUrls.PullRequest(owner, name, number), pullRequestUpdate, AcceptHeaders.DraftPullRequestApiPreview);
}
/// <summary>
@@ -252,7 +252,7 @@ namespace Octokit
{
Ensure.ArgumentNotNull(pullRequestUpdate, nameof(pullRequestUpdate));
return ApiConnection.Patch<PullRequest>(ApiUrls.PullRequest(repositoryId, number), pullRequestUpdate);
return ApiConnection.Patch<PullRequest>(ApiUrls.PullRequest(repositoryId, number), pullRequestUpdate, AcceptHeaders.DraftPullRequestApiPreview);
}
/// <summary>
+2
View File
@@ -49,6 +49,8 @@ namespace Octokit
public const string PullRequestReviewsApiPreview = "application/vnd.github.black-cat-preview+json";
public const string DraftPullRequestApiPreview = "application/vnd.github.shadow-cat-preview+json";
public const string ProjectsApiPreview = "application/vnd.github.inertia-preview+json";
public const string OrganizationMembershipPreview = "application/vnd.github.korra-preview+json";
+5
View File
@@ -50,6 +50,11 @@ namespace Octokit
/// Body of the pull request (optional)
/// </summary>
public string Body { get; set; }
/// <summary>
/// Whether the pull request is in a draft state or not (optional)
/// </summary>
public bool? Draft { get; set; }
internal string DebuggerDisplay
{
+7 -1
View File
@@ -16,7 +16,7 @@ namespace Octokit
Number = number;
}
public PullRequest(long id, string nodeId, string url, string htmlUrl, string diffUrl, string patchUrl, string issueUrl, string statusesUrl, int number, ItemState state, string title, string body, DateTimeOffset createdAt, DateTimeOffset updatedAt, DateTimeOffset? closedAt, DateTimeOffset? mergedAt, GitReference head, GitReference @base, User user, User assignee, IReadOnlyList<User> assignees, bool? mergeable, MergeableState? mergeableState, User mergedBy, string mergeCommitSha, int comments, int commits, int additions, int deletions, int changedFiles, Milestone milestone, bool locked, bool? maintainerCanModify, IReadOnlyList<User> requestedReviewers, IReadOnlyList<Label> labels)
public PullRequest(long id, string nodeId, string url, string htmlUrl, string diffUrl, string patchUrl, string issueUrl, string statusesUrl, int number, ItemState state, string title, string body, DateTimeOffset createdAt, DateTimeOffset updatedAt, DateTimeOffset? closedAt, DateTimeOffset? mergedAt, GitReference head, GitReference @base, User user, User assignee, IReadOnlyList<User> assignees, bool draft, bool? mergeable, MergeableState? mergeableState, User mergedBy, string mergeCommitSha, int comments, int commits, int additions, int deletions, int changedFiles, Milestone milestone, bool locked, bool? maintainerCanModify, IReadOnlyList<User> requestedReviewers, IReadOnlyList<Label> labels)
{
Id = id;
NodeId = nodeId;
@@ -39,6 +39,7 @@ namespace Octokit
User = user;
Assignee = assignee;
Assignees = assignees;
Draft = draft;
Mergeable = mergeable;
MergeableState = mergeableState;
MergedBy = mergedBy;
@@ -165,6 +166,11 @@ namespace Octokit
/// </summary>
public Milestone Milestone { get; protected set; }
/// <summary>
/// Whether or not the pull request is in a draft state, and cannot be merged.
/// </summary>
public bool Draft { get; protected set; }
/// <summary>
/// Whether or not the pull request has been merged.
/// </summary>