More integration tests for Issues and Milestones

This commit is contained in:
Haacked
2013-10-23 15:18:28 -07:00
parent e1d618dcaa
commit f89c25a03b
20 changed files with 876 additions and 15 deletions
+103 -11
View File
@@ -1,5 +1,7 @@
using System;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Octokit;
using Octokit.Tests.Integration;
@@ -9,6 +11,7 @@ public class IssuesClientTests : IDisposable
{
readonly IGitHubClient _gitHubClient;
readonly Repository _repository;
readonly IIssuesClient _issuesClient;
public IssuesClientTests()
{
@@ -17,49 +20,138 @@ public class IssuesClientTests : IDisposable
Credentials = Helper.Credentials
};
var repoName = Helper.MakeNameWithTimestamp("public-repo");
_issuesClient = _gitHubClient.Issue;
_repository = _gitHubClient.Repository.Create(new NewRepository { Name = repoName }).Result;
}
[IntegrationTest]
public async Task CanCreateRetrieveAndDeleteIssue()
public async Task CanCreateRetrieveAndCloseIssue()
{
string owner = _repository.Owner.Login;
var newIssue = new NewIssue("a test issue") { Body = "A new unassigned issue" };
var issue = await _gitHubClient.Issue.Create(owner, _repository.Name, newIssue);
var issue = await _issuesClient.Create(owner, _repository.Name, newIssue);
try
{
Assert.NotNull(issue);
var retrieved = await _gitHubClient.Issue.Get(owner, _repository.Name, issue.Number);
var all = await _gitHubClient.Issue.GetForRepository(owner, _repository.Name);
var retrieved = await _issuesClient.Get(owner, _repository.Name, issue.Number);
var all = await _issuesClient.GetForRepository(owner, _repository.Name);
Assert.NotNull(retrieved);
Assert.True(all.Any(i => i.Number == retrieved.Number));
}
finally
{
var closed = _gitHubClient.Issue.Update(owner, _repository.Name, issue.Number,
var closed = _issuesClient.Update(owner, _repository.Name, issue.Number,
new IssueUpdate { State = ItemState.Closed })
.Result;
Assert.NotNull(closed);
}
}
[IntegrationTest]
public async Task CanListOpenIssuesWithDefaultSort()
{
string owner = _repository.Owner.Login;
var newIssue1 = new NewIssue("A test issue1") { Body = "A new unassigned issue" };
var newIssue2 = new NewIssue("A test issue2") { Body = "A new unassigned issue" };
var newIssue3 = new NewIssue("A test issue3") { Body = "A new unassigned issue" };
var newIssue4 = new NewIssue("A test issue4") { Body = "A new unassigned issue" };
await _issuesClient.Create(owner, _repository.Name, newIssue1);
Thread.Sleep(1000);
await _issuesClient.Create(owner, _repository.Name, newIssue2);
Thread.Sleep(1000);
await _issuesClient.Create(owner, _repository.Name, newIssue3);
var closed = await _issuesClient.Create(owner, _repository.Name, newIssue4);
await _issuesClient.Update(owner, _repository.Name, closed.Number,
new IssueUpdate { State = ItemState.Closed });
var issues = await _issuesClient.GetForRepository(owner, _repository.Name);
Assert.Equal(3, issues.Count);
Assert.Equal("A test issue3", issues[0].Title);
Assert.Equal("A test issue2", issues[1].Title);
Assert.Equal("A test issue1", issues[2].Title);
}
[IntegrationTest]
public async Task CanListIssuesWithAscendingSort()
{
string owner = _repository.Owner.Login;
var newIssue1 = new NewIssue("A test issue1") { Body = "A new unassigned issue" };
var newIssue2 = new NewIssue("A test issue2") { Body = "A new unassigned issue" };
var newIssue3 = new NewIssue("A test issue3") { Body = "A new unassigned issue" };
var newIssue4 = new NewIssue("A test issue4") { Body = "A new unassigned issue" };
await _issuesClient.Create(owner, _repository.Name, newIssue1);
Thread.Sleep(1000);
await _issuesClient.Create(owner, _repository.Name, newIssue2);
Thread.Sleep(1000);
await _issuesClient.Create(owner, _repository.Name, newIssue3);
var closed = await _issuesClient.Create(owner, _repository.Name, newIssue4);
await _issuesClient.Update(owner, _repository.Name, closed.Number,
new IssueUpdate { State = ItemState.Closed });
var issues = await _issuesClient.GetForRepository(owner, _repository.Name,
new RepositoryIssueRequest {SortDirection = SortDirection.Ascending});
Assert.Equal(3, issues.Count);
Assert.Equal("A test issue1", issues[0].Title);
Assert.Equal("A test issue2", issues[1].Title);
Assert.Equal("A test issue3", issues[2].Title);
}
[IntegrationTest]
public async Task CanListClosedIssues()
{
string owner = _repository.Owner.Login;
var newIssue1 = new NewIssue("A test issue1") { Body = "A new unassigned issue" };
var newIssue2 = new NewIssue("A closed issue") { Body = "A new unassigned issue" };
await _issuesClient.Create(owner, _repository.Name, newIssue1);
await _issuesClient.Create(owner, _repository.Name, newIssue2);
var closed = await _issuesClient.Create(owner, _repository.Name, newIssue2);
await _issuesClient.Update(owner, _repository.Name, closed.Number,
new IssueUpdate { State = ItemState.Closed });
var issues = await _issuesClient.GetForRepository(owner, _repository.Name,
new RepositoryIssueRequest { State = ItemState.Closed });
Assert.Equal(1, issues.Count);
Assert.Equal("A closed issue", issues[0].Title);
}
[IntegrationTest]
public async Task CanListMilestoneIssues()
{
string owner = _repository.Owner.Login;
var milestone = await _issuesClient.Milestone.Create(owner, _repository.Name, new NewMilestone("milestone"));
var newIssue1 = new NewIssue("A test issue1") { Body = "A new unassigned issue" };
var newIssue2 = new NewIssue("A milestone issue") { Body = "A new unassigned issue", Milestone = milestone.Number };
await _issuesClient.Create(owner, _repository.Name, newIssue1);
await _issuesClient.Create(owner, _repository.Name, newIssue2);
var issues = await _issuesClient.GetForRepository(owner, _repository.Name,
new RepositoryIssueRequest { Milestone = milestone.Number.ToString(CultureInfo.InvariantCulture) });
Assert.Equal(1, issues.Count);
Assert.Equal("A milestone issue", issues[0].Title);
}
[IntegrationTest]
public async Task CanRetrieveClosedIssues()
{
string owner = _repository.Owner.Login;
var newIssue = new NewIssue("A test issue") { Body = "A new unassigned issue" };
var issue1 = await _gitHubClient.Issue.Create(owner, _repository.Name, newIssue);
var issue2 = await _gitHubClient.Issue.Create(owner, _repository.Name, newIssue);
await _gitHubClient.Issue.Update(owner, _repository.Name, issue1.Number,
var issue1 = await _issuesClient.Create(owner, _repository.Name, newIssue);
var issue2 = await _issuesClient.Create(owner, _repository.Name, newIssue);
await _issuesClient.Update(owner, _repository.Name, issue1.Number,
new IssueUpdate { State = ItemState.Closed });
await _gitHubClient.Issue.Update(owner, _repository.Name, issue2.Number,
await _issuesClient.Update(owner, _repository.Name, issue2.Number,
new IssueUpdate { State = ItemState.Closed });
var retrieved = await _gitHubClient.Issue.GetForRepository(owner, _repository.Name,
var retrieved = await _issuesClient.GetForRepository(owner, _repository.Name,
new RepositoryIssueRequest { State = ItemState.Closed });
Assert.True(retrieved.Count >= 2);
@@ -0,0 +1,124 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Octokit;
using Octokit.Tests.Integration;
using Xunit;
public class MilestonesClientTests : IDisposable
{
readonly IGitHubClient _gitHubClient;
readonly IMilestonesClient _milestonesClient;
readonly Repository _repository;
readonly string _repositoryOwner;
readonly string _repositoryName;
public MilestonesClientTests()
{
_gitHubClient = new GitHubClient("Test Runner User Agent")
{
Credentials = Helper.Credentials
};
_milestonesClient = _gitHubClient.Issue.Milestone;
var repoName = Helper.MakeNameWithTimestamp("public-repo");
_repository = _gitHubClient.Repository.Create(new NewRepository { Name = repoName }).Result;
_repositoryOwner = _repository.Owner.Login;
_repositoryName = _repository.Name;
}
[IntegrationTest]
public async Task CanRetrieveOneMilestone()
{
var newMilestone = new NewMilestone("a milestone") { DueOn = DateTime.Now };
var created = await _milestonesClient.Create(_repositoryOwner, _repositoryName, newMilestone);
var result = await _milestonesClient.Get(_repositoryOwner, _repositoryName, created.Number);
Assert.Equal("a milestone", result.Title);
}
[IntegrationTest]
public async Task CanListEmptyMilestones()
{
var milestones = await _milestonesClient.GetForRepository(_repositoryOwner, _repositoryName);
Assert.Empty(milestones);
}
[IntegrationTest]
public async Task CanListMilestonesWithDefaultSortByDueDateAsc()
{
var milestone1 = new NewMilestone("milestone 1") { DueOn = DateTime.Now };
var milestone2 = new NewMilestone("milestone 2") { DueOn = DateTime.Now.AddDays(1) };
var milestone3 = new NewMilestone("milestone 3") { DueOn = DateTime.Now.AddDays(3), State = ItemState.Closed };
await _milestonesClient.Create(_repositoryOwner, _repositoryName, milestone1);
await _milestonesClient.Create(_repositoryOwner, _repositoryName, milestone2);
await _milestonesClient.Create(_repositoryOwner, _repositoryName, milestone3);
var milestones = await _milestonesClient.GetForRepository(_repositoryOwner, _repositoryName);
Assert.Equal(2, milestones.Count);
Assert.Equal("milestone 1", milestones[0].Title);
Assert.Equal("milestone 2", milestones[1].Title);
}
[IntegrationTest]
public async Task CanListMilestonesWithSortByDueDateDesc()
{
var milestone1 = new NewMilestone("milestone 1") { DueOn = DateTime.Now };
var milestone2 = new NewMilestone("milestone 2") { DueOn = DateTime.Now.AddDays(1) };
var milestone3 = new NewMilestone("milestone 3") { DueOn = DateTime.Now.AddDays(3), State = ItemState.Closed };
await _milestonesClient.Create(_repositoryOwner, _repositoryName, milestone1);
await _milestonesClient.Create(_repositoryOwner, _repositoryName, milestone2);
await _milestonesClient.Create(_repositoryOwner, _repositoryName, milestone3);
var milestones = await _milestonesClient.GetForRepository(_repositoryOwner, _repositoryName,
new MilestoneRequest { SortDirection = SortDirection.Descending });
Assert.Equal(2, milestones.Count);
Assert.Equal("milestone 2", milestones[0].Title);
Assert.Equal("milestone 1", milestones[1].Title);
}
[IntegrationTest]
public async Task CanListClosedMilestones()
{
var milestone1 = new NewMilestone("milestone 1") { DueOn = DateTime.Now };
var milestone2 = new NewMilestone("milestone 2") { DueOn = DateTime.Now.AddDays(1) };
var milestone3 = new NewMilestone("milestone 3") { DueOn = DateTime.Now.AddDays(3), State = ItemState.Closed };
await _milestonesClient.Create(_repositoryOwner, _repositoryName, milestone1);
await _milestonesClient.Create(_repositoryOwner, _repositoryName, milestone2);
await _milestonesClient.Create(_repositoryOwner, _repositoryName, milestone3);
var milestones = await _milestonesClient.GetForRepository(_repositoryOwner, _repositoryName,
new MilestoneRequest { State = ItemState.Closed });
Assert.Equal(1, milestones.Count);
Assert.Equal("milestone 3", milestones[0].Title);
}
[IntegrationTest]
public async Task CanRetrieveClosedIssues()
{
string owner = _repository.Owner.Login;
var newIssue = new NewIssue("A test issue") { Body = "A new unassigned issue" };
var issue1 = await _gitHubClient.Issue.Create(owner, _repository.Name, newIssue);
var issue2 = await _gitHubClient.Issue.Create(owner, _repository.Name, newIssue);
await _gitHubClient.Issue.Update(owner, _repository.Name, issue1.Number,
new IssueUpdate { State = ItemState.Closed });
await _gitHubClient.Issue.Update(owner, _repository.Name, issue2.Number,
new IssueUpdate { State = ItemState.Closed });
var retrieved = await _gitHubClient.Issue.GetForRepository(owner, _repository.Name,
new RepositoryIssueRequest { State = ItemState.Closed });
Assert.True(retrieved.Count >= 2);
Assert.True(retrieved.Any(i => i.Number == issue1.Number));
Assert.True(retrieved.Any(i => i.Number == issue2.Number));
}
public void Dispose()
{
Helper.DeleteRepo(_repository);
}
}
@@ -56,6 +56,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AssigneesClientTests.cs" />
<Compile Include="MilestonesClientTests.cs" />
<Compile Include="IntegrationTestAttribute.cs" />
<Compile Include="IssuesClientTests.cs" />
<Compile Include="MiscellaneousClientTests.cs" />
@@ -0,0 +1,171 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using NSubstitute;
using Octokit;
using Octokit.Tests;
using Octokit.Tests.Helpers;
using Xunit;
public class MilestonesClientTests
{
public class TheGetMethod
{
[Fact]
public void RequestsCorrectUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new MilestonesClient(connection);
client.Get("fake", "repo", 42);
connection.Received().Get<Milestone>(Arg.Is<Uri>(u => u.ToString() == "/repos/fake/repo/milestones/42"),
null);
}
[Fact]
public async Task EnsuresNonNullArguments()
{
var client = new MilestonesClient(Substitute.For<IApiConnection>());
await AssertEx.Throws<ArgumentNullException>(async () => await client.Get(null, "name", 1));
await AssertEx.Throws<ArgumentNullException>(async () => await client.Get("owner", null, 1));
await AssertEx.Throws<ArgumentException>(async () => await client.Get(null, "", 1));
await AssertEx.Throws<ArgumentException>(async () => await client.Get("", null, 1));
}
}
public class TheGetForRepositoryMethod
{
[Fact]
public async Task RequestsCorrectUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new MilestonesClient(connection);
await client.GetForRepository("fake", "repo");
connection.Received().GetAll<Milestone>(Arg.Is<Uri>(u => u.ToString() == "/repos/fake/repo/milestones"),
Args.EmptyDictionary);
}
[Fact]
public void SendsAppropriateParameters()
{
var connection = Substitute.For<IApiConnection>();
var client = new MilestonesClient(connection);
client.GetForRepository("fake", "repo", new MilestoneRequest { SortDirection = SortDirection.Descending });
connection.Received().GetAll<Milestone>(Arg.Is<Uri>(u => u.ToString() == "/repos/fake/repo/milestones"),
Arg.Is<Dictionary<string, string>>(d => d["direction"] == "desc" && d.Count == 1));
}
}
public class TheCreateMethod
{
[Fact]
public void PostsToCorrectUrl()
{
var newIssue = new NewIssue("some title");
var connection = Substitute.For<IApiConnection>();
var client = new IssuesClient(connection);
client.Create("fake", "repo", newIssue);
connection.Received().Post<Issue>(Arg.Is<Uri>(u => u.ToString() == "/repos/fake/repo/issues"),
newIssue);
}
[Fact]
public async Task EnsuresArgumentsNotNull()
{
var connection = Substitute.For<IApiConnection>();
var client = new IssuesClient(connection);
AssertEx.Throws<ArgumentNullException>(async () => await
client.Create(null, "name", new NewIssue("title")));
AssertEx.Throws<ArgumentException>(async () => await
client.Create("", "name", new NewIssue("x")));
AssertEx.Throws<ArgumentNullException>(async () => await
client.Create("owner", null, new NewIssue("x")));
AssertEx.Throws<ArgumentException>(async () => await
client.Create("owner", "", new NewIssue("x")));
AssertEx.Throws<ArgumentNullException>(async () => await
client.Create("owner", "name", null));
}
}
public class TheUpdateMethod
{
[Fact]
public void PostsToCorrectUrl()
{
var milestoneUpdate = new MilestoneUpdate();
var connection = Substitute.For<IApiConnection>();
var client = new MilestonesClient(connection);
client.Update("fake", "repo", 42, milestoneUpdate);
connection.Received().Patch<Milestone>(Arg.Is<Uri>(u => u.ToString() == "/repos/fake/repo/milestones/42"),
milestoneUpdate);
}
[Fact]
public async Task EnsuresArgumentsNotNull()
{
var connection = Substitute.For<IApiConnection>();
var client = new MilestonesClient(connection);
AssertEx.Throws<ArgumentNullException>(async () => await
client.Create(null, "name", new NewMilestone("title")));
AssertEx.Throws<ArgumentException>(async () => await
client.Create("", "name", new NewMilestone("x")));
AssertEx.Throws<ArgumentNullException>(async () => await
client.Create("owner", null, new NewMilestone("x")));
AssertEx.Throws<ArgumentException>(async () => await
client.Create("owner", "", new NewMilestone("x")));
AssertEx.Throws<ArgumentNullException>(async () => await
client.Create("owner", "name", null));
}
}
public class TheDeleteMethod
{
[Fact]
public void PostsToCorrectUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new MilestonesClient(connection);
client.Delete("fake", "repo", 42);
connection.Received().Delete(Arg.Is<Uri>(u => u.ToString() == "/repos/fake/repo/milestones/42"));
}
[Fact]
public async Task EnsuresArgumentsNotNull()
{
var connection = Substitute.For<IApiConnection>();
var client = new MilestonesClient(connection);
AssertEx.Throws<ArgumentNullException>(async () => await
client.Delete(null, "name", 42));
AssertEx.Throws<ArgumentException>(async () => await
client.Delete("", "name", 42));
AssertEx.Throws<ArgumentNullException>(async () => await
client.Delete("owner", null, 42));
AssertEx.Throws<ArgumentException>(async () => await
client.Delete("owner", "", 42));
}
}
public class TheCtor
{
[Fact]
public void EnsuresArgument()
{
Assert.Throws<ArgumentNullException>(() => new MilestonesClient(null));
}
}
}
@@ -0,0 +1,59 @@
using Octokit;
using Xunit;
public class MilestoneRequestTests
{
public class TheToParametersDictionaryMethod
{
[Fact]
public void OnlyContainsChangedValues()
{
var request = new MilestoneRequest { SortDirection = SortDirection.Descending };
var parameters = request.ToParametersDictionary();
Assert.Equal(1, parameters.Count);
Assert.Equal("desc", parameters["direction"]);
}
[Fact]
public void ContainsSetValues()
{
var request = new MilestoneRequest
{
State = ItemState.Closed,
SortProperty = MilestoneSort.Completeness,
SortDirection = SortDirection.Descending,
};
var parameters = request.ToParametersDictionary();
Assert.Equal("closed", parameters["state"]);
Assert.Equal("completeness", parameters["sort"]);
Assert.Equal("desc", parameters["direction"]);
}
[Fact]
public void DoesNotAddDefaultAscendingSort()
{
var request = new MilestoneRequest
{
SortDirection = SortDirection.Ascending,
};
var parameters = request.ToParametersDictionary();
Assert.Empty(parameters);
}
[Fact]
public void ReturnsEmptyDictionaryForDefaultRequest()
{
var request = new MilestoneRequest();
var parameters = request.ToParametersDictionary();
Assert.Empty(parameters);
}
}
}
+2
View File
@@ -61,6 +61,7 @@
<ItemGroup>
<Compile Include="Authentication\CredentialsTests.cs" />
<Compile Include="Clients\AssigneesClientTests.cs" />
<Compile Include="Clients\MilestonesClientTests.cs" />
<Compile Include="Clients\IssuesClientTests.cs" />
<Compile Include="Clients\NotificationsClientTests.cs" />
<Compile Include="Clients\ReleasesClientTests.cs" />
@@ -93,6 +94,7 @@
<Compile Include="Http\RateLimitTests.cs" />
<Compile Include="Http\ResponseTests.cs" />
<Compile Include="Http\RequestTests.cs" />
<Compile Include="Models\MilestoneRequestTests.cs" />
<Compile Include="Models\IssueRequestTests.cs" />
<Compile Include="Models\ModelExtensionsTests.cs" />
<Compile Include="Models\ReadOnlyPagedCollectionTests.cs" />
+2
View File
@@ -53,6 +53,7 @@
<Compile Include="Authentication\CredentialsTests.cs" />
<Compile Include="Clients\AssigneesClientTests.cs" />
<Compile Include="Clients\IssuesClientTests.cs" />
<Compile Include="Clients\MilestonesClientTests.cs" />
<Compile Include="Clients\MiscellaneousClientTests.cs" />
<Compile Include="Clients\NotificationsClientTests.cs" />
<Compile Include="Clients\ReleasesClientTests.cs" />
@@ -85,6 +86,7 @@
<Compile Include="Http\ResponseTests.cs" />
<Compile Include="Http\RequestTests.cs" />
<Compile Include="Models\IssueRequestTests.cs" />
<Compile Include="Models\MilestoneRequestTests.cs" />
<Compile Include="Models\ModelExtensionsTests.cs" />
<Compile Include="Models\ReadOnlyPagedCollectionTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
+2
View File
@@ -8,9 +8,11 @@ namespace Octokit
public IssuesClient(IApiConnection apiConnection) : base(apiConnection)
{
Assignee = new AssigneesClient(apiConnection);
Milestone = new MilestonesClient(apiConnection);
}
public IAssigneesClient Assignee { get; private set; }
public IMilestonesClient Milestone { get; private set; }
/// <summary>
/// Gets a single Issue by number./// </summary>
+119
View File
@@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Octokit
{
public class MilestonesClient : ApiClient, IMilestonesClient
{
public MilestonesClient(IApiConnection apiConnection) : base(apiConnection)
{
}
/// <summary>
/// Gets all Milestones across all the authenticated users visible repositories including owned repositories,
/// member repositories, and organization repositories.
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/Milestones/#get-a-single-Milestone
/// </remarks>
/// <returns></returns>
public async Task<Milestone> Get(string owner, string name, int number)
{
Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
Ensure.ArgumentNotNullOrEmptyString(name, "name");
return await ApiConnection.Get<Milestone>(ApiUrls.Milestone(owner, name, number));
}
/// <summary>
/// Gets all open milestones for the repository.
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/Milestones/#list-Milestones-for-a-repository
/// </remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <returns></returns>
public async Task<IReadOnlyList<Milestone>> GetForRepository(string owner, string name)
{
return await GetForRepository(owner, name, new MilestoneRequest());
}
/// <summary>
/// Gets all open milestones for the repository.
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/Milestones/#list-Milestones-for-a-repository
/// </remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="request">Used to filter and sort the list of Milestones returned</param>
/// <returns></returns>
public async Task<IReadOnlyList<Milestone>> GetForRepository(string owner, string name, MilestoneRequest request)
{
Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
Ensure.ArgumentNotNullOrEmptyString(name, "name");
Ensure.ArgumentNotNull(request, "request");
return await ApiConnection.GetAll<Milestone>(ApiUrls.Milestones(owner, name),
request.ToParametersDictionary());
}
/// <summary>
/// Creates an Milestone for the specified repository. Any user with pull access to a repository can create an
/// Milestone.
/// </summary>
/// <remarks>http://developer.github.com/v3/Milestones/#create-an-Milestone</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="newMilestone">A <see cref="NewMilestone"/> instance describing the new Milestone to create</param>
/// <returns></returns>
public async Task<Milestone> Create(string owner, string name, NewMilestone newMilestone)
{
Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
Ensure.ArgumentNotNullOrEmptyString(name, "name");
Ensure.ArgumentNotNull(newMilestone, "newMilestone");
return await ApiConnection.Post<Milestone>(ApiUrls.Milestones(owner, name), newMilestone);
}
/// <summary>
/// Creates an Milestone for the specified repository. Any user with pull access to a repository can create an
/// Milestone.
/// </summary>
/// <remarks>http://developer.github.com/v3/Milestones/#create-an-Milestone</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="number">The Milestone number</param>
/// <param name="milestoneUpdate">An <see cref="MilestoneUpdate"/> instance describing the changes to make to the Milestone
/// </param>
/// <returns></returns>
public async Task<Milestone> Update(string owner, string name, int number, MilestoneUpdate milestoneUpdate)
{
Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
Ensure.ArgumentNotNullOrEmptyString(name, "name");
Ensure.ArgumentNotNull(milestoneUpdate, "milestoneUpdate");
return await ApiConnection.Patch<Milestone>(ApiUrls.Milestone(owner, name, number), milestoneUpdate);
}
/// <summary>
/// Deletes a milestone for the specified repository. Any user with pull access to a repository can create an
/// Milestone.
/// </summary>
/// <remarks>http://developer.github.com/v3/Milestones/#delete-a-milestone</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="number">The milestone number</param>
/// <returns></returns>
public async Task Delete(string owner, string name, int number)
{
Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
Ensure.ArgumentNotNullOrEmptyString(name, "name");
await ApiConnection.Delete(ApiUrls.Milestone(owner, name, number));
}
}
}
+1 -1
View File
@@ -6,7 +6,7 @@ namespace Octokit
{
public class NotificationsClient : ApiClient, INotificationsClient
{
public NotificationsClient(IApiConnection client) : base(client)
public NotificationsClient(IApiConnection apiConnection) : base(apiConnection)
{
}
+23
View File
@@ -211,5 +211,28 @@ namespace Octokit
{
return "/repos/{0}/{1}/assignees/{2}".FormatUri(owner, name, login);
}
/// <summary>
/// Returns the <see cref="Uri"/> that returns the specified milestone.
/// </summary>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// /// <param name="number">The milestone number</param>
/// <returns></returns>
public static Uri Milestone(string owner, string name, int number)
{
return "/repos/{0}/{1}/milestones/{2}".FormatUri(owner, name, number);
}
/// <summary>
/// Returns the <see cref="Uri"/> that returns all of the milestones for the specified repository.
/// </summary>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <returns></returns>
public static Uri Milestones(string owner, string name)
{
return "/repos/{0}/{1}/milestones".FormatUri(owner, name);
}
}
}
+5
View File
@@ -8,6 +8,11 @@ namespace Octokit
{
IAssigneesClient Assignee { get; }
/// <summary>
/// Client for managing milestones.
/// </summary>
IMilestonesClient Milestone { get; }
/// <summary>
/// Gets a single Issue by number.
/// </summary>
+79
View File
@@ -0,0 +1,79 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
namespace Octokit
{
public interface IMilestonesClient
{
/// <summary>
/// Gets all Milestones across all the authenticated users visible repositories including owned repositories,
/// member repositories, and organization repositories.
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/Milestones/#get-a-single-Milestone
/// </remarks>
/// <returns></returns>
[SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get",
Justification = "Method makes a network request")]
Task<Milestone> Get(string owner, string name, int number);
/// <summary>
/// Gets all open milestones for the repository.
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/Milestones/#list-Milestones-for-a-repository
/// </remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <returns></returns>
Task<IReadOnlyList<Milestone>> GetForRepository(string owner, string name);
/// <summary>
/// Gets all open milestones for the repository.
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/Milestones/#list-Milestones-for-a-repository
/// </remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="request">Used to filter and sort the list of Milestones returned</param>
/// <returns></returns>
Task<IReadOnlyList<Milestone>> GetForRepository(string owner, string name, MilestoneRequest request);
/// <summary>
/// Creates a milestone for the specified repository. Any user with pull access to a repository can create an
/// Milestone.
/// </summary>
/// <remarks>http://developer.github.com/v3/Milestones/#create-an-Milestone</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="newMilestone">A <see cref="NewMilestone"/> instance describing the new Milestone to create</param>
/// <returns></returns>
Task<Milestone> Create(string owner, string name, NewMilestone newMilestone);
/// <summary>
/// Creates a milestone for the specified repository. Any user with pull access to a repository can create an
/// Milestone.
/// </summary>
/// <remarks>http://developer.github.com/v3/Milestones/#update-a-milestone</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="number">The Milestone number</param>
/// <param name="milestoneUpdate">An <see cref="MilestoneUpdate"/> instance describing the changes to make to the Milestone
/// </param>
/// <returns></returns>
Task<Milestone> Update(string owner, string name, int number, MilestoneUpdate milestoneUpdate);
/// <summary>
/// Deletes a milestone for the specified repository. Any user with pull access to a repository can create an
/// Milestone.
/// </summary>
/// <remarks>http://developer.github.com/v3/Milestones/#delete-a-milestone</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="number">The milestone number</param>
/// <returns></returns>
Task Delete(string owner, string name, int number);
}
}
+5 -3
View File
@@ -38,17 +38,19 @@ namespace Octokit
if (Filter != _defaultParameterValues.Filter)
{
parameters.Add("filter", Enum.GetName(typeof(IssueFilter), Filter).ToLowerInvariant());
var filter = Enum.GetName(typeof(IssueFilter), Filter) ?? "filter";
parameters.Add("filter", filter.ToLowerInvariant());
}
if (State != _defaultParameterValues.State)
{
parameters.Add("state", Enum.GetName(typeof(ItemState), State).ToLowerInvariant());
parameters.Add("state", "closed");
}
if (SortProperty != _defaultParameterValues.SortProperty)
{
parameters.Add("sort", Enum.GetName(typeof(IssueSort), SortProperty).ToLowerInvariant());
var sort = Enum.GetName(typeof(IssueSort), SortProperty) ?? "created";
parameters.Add("sort", sort.ToLowerInvariant());
}
if (SortDirection != _defaultParameterValues.SortDirection)
+39
View File
@@ -4,15 +4,54 @@ namespace Octokit
{
public class Milestone
{
/// <summary>
/// The URL for this milestone.
/// </summary>
public Uri Url { get; set; }
/// <summary>
/// The milestone number.
/// </summary>
public int Number { get; set; }
/// <summary>
/// Whether the milestone is open or closed.
/// </summary>
public ItemState State { get; set; }
/// <summary>
/// Title of the milestone
/// </summary>
public string Title { get; set; }
/// <summary>
/// Optional description for the milestone.
/// </summary>
public string Description { get; set; }
/// <summary>
/// The user that created this milestone.
/// </summary>
public User Creator { get; set; }
/// <summary>
/// The number of open issues in this milestone.
/// </summary>
public int OpenIssues { get; set; }
/// <summary>
/// The number of closed issues in this milestone.
/// </summary>
public int ClosedIssues { get; set; }
/// <summary>
/// The date this milestone was created
/// </summary>
public DateTimeOffset CreatedAt { get; set; }
/// <summary>
/// The date, if any, when this milestone is due.
/// </summary>
public DateTimeOffset? DueOn { get; set; }
}
}
+57
View File
@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace Octokit
{
public class MilestoneRequest
{
static readonly MilestoneRequest _defaultParameterValues = new MilestoneRequest();
public MilestoneRequest()
{
State = ItemState.Open;
SortProperty = MilestoneSort.DueDate;
SortDirection = SortDirection.Ascending;
}
public ItemState State { get; set; }
public MilestoneSort SortProperty { get; set; }
public SortDirection SortDirection { get; set; }
/// <summary>
/// Returns a dictionary of query string parameters that represent this request. Only values that
/// do not have default values are in the dictionary. If everything is default, this returns an
/// empty dictionary.
/// </summary>
[SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase",
Justification = "The API expects lowercase")]
public virtual IDictionary<string, string> ToParametersDictionary()
{
var parameters = new Dictionary<string, string>();
if (State != _defaultParameterValues.State)
{
parameters.Add("state", "closed");
}
if (SortProperty != _defaultParameterValues.SortProperty)
{
parameters.Add("sort", "completeness");
}
if (SortDirection != _defaultParameterValues.SortDirection)
{
parameters.Add("direction", "desc");
}
return parameters;
}
}
public enum MilestoneSort
{
DueDate,
Completeness
}
}
+36
View File
@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Octokit
{
public class MilestoneUpdate
{
/// <summary>
/// The milestone number.
/// </summary>
public int Number { get; set; }
/// <summary>
/// Title of the milestone (required)
/// </summary>
public string Title { get; set; }
/// <summary>
/// Whether the milestone is open or closed. The default is <see cref="ItemState.Open"/>.
/// </summary>
public ItemState State { get; set; }
/// <summary>
/// Optional description for the milestone.
/// </summary>
public string Description { get; set; }
/// <summary>
/// An optional date when the milestone is due.
/// </summary>
public DateTimeOffset? DueOn { get; set; }
}
}
+38
View File
@@ -0,0 +1,38 @@
using System;
namespace Octokit
{
/// <summary>
/// Describes a new milestone to create via the <see cref="IMilestonesClient.Create(NewMilestone)"/> method.
/// </summary>
public class NewMilestone
{
public NewMilestone(string title)
{
Ensure.ArgumentNotNull(title, "title");
Title = title;
State = ItemState.Open;
}
/// <summary>
/// Title of the milestone (required)
/// </summary>
public string Title { get; private set; }
/// <summary>
/// Whether the milestone is open or closed. The default is <see cref="ItemState.Open"/>.
/// </summary>
public ItemState State { get; set; }
/// <summary>
/// Optional description for the milestone.
/// </summary>
public string Description { get; set; }
/// <summary>
/// An optional date when the milestone is due.
/// </summary>
public DateTimeOffset? DueOn { get; set; }
}
}
+5
View File
@@ -83,9 +83,13 @@
</Compile>
<Compile Include="Clients\AssigneesClient.cs" />
<Compile Include="Clients\IssuesClient.cs" />
<Compile Include="Clients\MilestonesClient.cs" />
<Compile Include="Exceptions\NotFoundException.cs" />
<Compile Include="IAssigneesClient.cs" />
<Compile Include="IIssuesClient.cs" />
<Compile Include="IMilestonesClient.cs" />
<Compile Include="Models\MilestoneUpdate.cs" />
<Compile Include="Models\NewMilestone.cs" />
<Compile Include="Models\Issue.cs" />
<Compile Include="Models\IssueRequest.cs" />
<Compile Include="Models\IssueUpdate.cs" />
@@ -96,6 +100,7 @@
<Compile Include="Models\NotificationInfo.cs" />
<Compile Include="Models\PullRequest.cs" />
<Compile Include="Models\RepositoryIssueRequest.cs" />
<Compile Include="Models\MilestoneRequest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Exceptions\TwoFactorChallengeFailedException.cs" />
<Compile Include="Exceptions\TwoFactorRequiredException.cs" />
+5
View File
@@ -113,6 +113,7 @@
<Compile Include="Clients\AssigneesClient.cs" />
<Compile Include="Clients\AuthorizationsClient.cs" />
<Compile Include="Clients\IssuesClient.cs" />
<Compile Include="Clients\MilestonesClient.cs" />
<Compile Include="Clients\MiscellaneousClient.cs" />
<Compile Include="Clients\NotificationsClient.cs" />
<Compile Include="Clients\OrganizationsClient.cs" />
@@ -151,6 +152,7 @@
<Compile Include="IAuthorizationsClient.cs" />
<Compile Include="IGitHubClient.cs" />
<Compile Include="IIssuesClient.cs" />
<Compile Include="IMilestonesClient.cs" />
<Compile Include="IMiscellaneousClient.cs" />
<Compile Include="INotificationsClient.cs" />
<Compile Include="IOrganizationsClient.cs" />
@@ -190,8 +192,11 @@
<Compile Include="Models\IssueUpdate.cs" />
<Compile Include="Models\Label.cs" />
<Compile Include="Models\Milestone.cs" />
<Compile Include="Models\MilestoneRequest.cs" />
<Compile Include="Models\MilestoneUpdate.cs" />
<Compile Include="Models\NewAuthorization.cs" />
<Compile Include="Models\NewIssue.cs" />
<Compile Include="Models\NewMilestone.cs" />
<Compile Include="Models\NewRepository.cs" />
<Compile Include="Models\Notification.cs" />
<Compile Include="Models\NotificationInfo.cs" />