Implement AssigneesClient

Implement client to list and check available assignees for a repository
This commit is contained in:
Haacked
2013-10-22 17:24:35 -07:00
parent 70b94187b3
commit e1d618dcaa
18 changed files with 309 additions and 10 deletions

View File

@@ -0,0 +1,24 @@
using System.Threading.Tasks;
namespace Octokit
{
public interface IAssigneesClient
{
/// <summary>
/// Gets all the available assignees (owner + collaborators) to which issues may be assigned.
/// </summary>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <returns></returns>
Task<IReadOnlyList<User>> GetForRepository(string owner, string name);
/// <summary>
/// Checks to see if a user is an assignee for a repository.
/// </summary>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="assignee">Username of the prospective assignee</param>
/// <returns></returns>
Task<bool> CheckAssignee(string owner, string name, string assignee);
}
}

View File

@@ -93,6 +93,7 @@
<Compile Include="Clients\ObservableRepositoriesClient.cs" />
<Compile Include="Clients\ObservableSshKeysClient.cs" />
<Compile Include="Clients\ObservableUsersClient.cs" />
<Compile Include="IAssigneesClient.cs" />
<Compile Include="IObservableNotificationsClient.cs" />
<Compile Include="Helpers\AuthorizationExtensions.cs" />
<Compile Include="Helpers\ConnectionExtensions.cs" />

View File

@@ -0,0 +1,50 @@
using System.Linq;
using System.Threading.Tasks;
using Octokit;
using Octokit.Tests.Integration;
using Xunit;
public class AssigneesClientTests
{
readonly IGitHubClient _gitHubClient;
readonly Repository _repository;
readonly string _owner;
public AssigneesClientTests()
{
_gitHubClient = new GitHubClient("Test Runner User Agent")
{
Credentials = Helper.Credentials
};
var repoName = Helper.MakeNameWithTimestamp("public-repo");
_repository = _gitHubClient.Repository.Create(new NewRepository { Name = repoName }).Result;
_owner = _repository.Owner.Login;
}
[IntegrationTest]
public async Task CanCheckAssignees()
{
var isAssigned = await
_gitHubClient.Issue.Assignee.CheckAssignee(_owner, _repository.Name, "FakeHaacked");
Assert.False(isAssigned);
// Repository owner is always an assignee
isAssigned = await
_gitHubClient.Issue.Assignee.CheckAssignee(_owner, _repository.Name, _owner);
Assert.True(isAssigned);
}
[IntegrationTest]
public async Task CanListAssignees()
{
// Repository owner is always an assignee
var assignees = await _gitHubClient.Issue.Assignee.GetForRepository(_owner, _repository.Name);
Assert.True(assignees.Any(u => u.Login == Helper.Credentials.Login));
}
public void Dispose()
{
Helper.DeleteRepo(_repository);
}
}

View File

@@ -55,6 +55,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="AssigneesClientTests.cs" />
<Compile Include="IntegrationTestAttribute.cs" />
<Compile Include="IssuesClientTests.cs" />
<Compile Include="MiscellaneousClientTests.cs" />

View File

@@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using NSubstitute;
using Octokit;
using Octokit.Internal;
using Octokit.Tests.Helpers;
using Xunit;
using Xunit.Extensions;
public class AssignessClientTests
{
public class TheGetForRepositoryMethod
{
[Fact]
public void RequestsCorrectUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new AssigneesClient(connection);
client.GetForRepository("fake", "repo");
connection.Received().GetAll<User>(Arg.Is<Uri>(u => u.ToString() == "/repos/fake/repo/assignees"));
}
[Fact]
public async Task EnsuresNonNullArguments()
{
var client = new AssigneesClient(Substitute.For<IApiConnection>());
await AssertEx.Throws<ArgumentNullException>(async () => await client.GetForRepository(null, "name"));
await AssertEx.Throws<ArgumentException>(async () => await client.GetForRepository(null, ""));
await AssertEx.Throws<ArgumentNullException>(async () => await client.GetForRepository("owner", null));
await AssertEx.Throws<ArgumentException>(async () => await client.GetForRepository("", null));
}
}
public class TheCheckAssigneeMethod
{
[Theory]
[InlineData(HttpStatusCode.NoContent, true)]
[InlineData(HttpStatusCode.NotFound, false)]
public async Task RequestsCorrectValueForStatusCode(HttpStatusCode status, bool expected)
{
var response = Task.Factory.StartNew<IResponse<object>>(() =>
new ApiResponse<object> { StatusCode = status });
var connection = Substitute.For<IConnection>();
connection.GetAsync<object>(Arg.Is<Uri>(u => u.ToString() == "/repos/foo/bar/assignees/cody"),
null, null).Returns(response);
var apiConnection = Substitute.For<IApiConnection>();
apiConnection.Connection.Returns(connection);
var client = new AssigneesClient(apiConnection);
var result = await client.CheckAssignee("foo", "bar", "cody");
Assert.Equal(expected, result);
}
[Fact]
public async Task ThrowsExceptionForInvalidStatusCode()
{
var response = Task.Factory.StartNew<IResponse<object>>(() =>
new ApiResponse<object> { StatusCode = HttpStatusCode.Conflict });
var connection = Substitute.For<IConnection>();
connection.GetAsync<object>(Arg.Is<Uri>(u => u.ToString() == "/repos/foo/bar/assignees/cody"),
null, null).Returns(response);
var apiConnection = Substitute.For<IApiConnection>();
apiConnection.Connection.Returns(connection);
var client = new AssigneesClient(apiConnection);
AssertEx.Throws<ApiException>(async () => await client.CheckAssignee("foo", "bar", "cody"));
}
[Fact]
public async Task EnsuresNonNullArguments()
{
var client = new AssigneesClient(Substitute.For<IApiConnection>());
await AssertEx.Throws<ArgumentNullException>(async () => await client.CheckAssignee(null, "name", "tweety"));
await AssertEx.Throws<ArgumentException>(async () => await client.CheckAssignee(null, "", "tweety"));
await AssertEx.Throws<ArgumentNullException>(async () => await client.CheckAssignee("owner", null, "tweety"));
await AssertEx.Throws<ArgumentException>(async () => await client.CheckAssignee("", null, "tweety"));
await AssertEx.Throws<ArgumentNullException>(async () => await client.CheckAssignee("owner", "name", null));
await AssertEx.Throws<ArgumentException>(async () => await client.CheckAssignee("owner", "name", ""));
}
}
public class TheCtor
{
[Fact]
public void EnsuresArgument()
{
Assert.Throws<ArgumentNullException>(() => new AssigneesClient(null));
}
}
}

View File

@@ -60,6 +60,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Authentication\CredentialsTests.cs" />
<Compile Include="Clients\AssigneesClientTests.cs" />
<Compile Include="Clients\IssuesClientTests.cs" />
<Compile Include="Clients\NotificationsClientTests.cs" />
<Compile Include="Clients\ReleasesClientTests.cs" />

View File

@@ -51,6 +51,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Authentication\CredentialsTests.cs" />
<Compile Include="Clients\AssigneesClientTests.cs" />
<Compile Include="Clients\IssuesClientTests.cs" />
<Compile Include="Clients\MiscellaneousClientTests.cs" />
<Compile Include="Clients\NotificationsClientTests.cs" />

View File

@@ -14,6 +14,7 @@
Ensure.ArgumentNotNull(apiConnection, "apiConnection");
ApiConnection = apiConnection;
Connection = apiConnection.Connection;
}
/// <summary>
@@ -23,5 +24,11 @@
/// The API client's connection
/// </value>
protected IApiConnection ApiConnection {get; private set;}
/// <summary>
/// Returns the underlying <see cref="IConnection"/> used by the <see cref="IApiConnection"/>. This is useful
/// for requests that need to access the HTTP response and not just the response model.
/// </summary>
protected IConnection Connection { get; private set; }
}
}

View File

@@ -0,0 +1,55 @@
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
namespace Octokit
{
public class AssigneesClient : ApiClient, IAssigneesClient
{
public AssigneesClient(IApiConnection apiConnection) : base(apiConnection)
{
}
/// <summary>
/// Gets all the available assignees (owner + collaborators) to which issues may be assigned.
/// </summary>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <returns></returns>
public async Task<IReadOnlyList<User>> GetForRepository(string owner, string name)
{
Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
Ensure.ArgumentNotNullOrEmptyString(name, "name");
return await ApiConnection.GetAll<User>(ApiUrls.Assignees(owner, name));
}
/// <summary>
/// Checks to see if a user is an assignee for a repository.
/// </summary>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="assignee">Username of the prospective assignee</param>
/// <returns></returns>
public async Task<bool> CheckAssignee(string owner, string name, string assignee)
{
Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
Ensure.ArgumentNotNullOrEmptyString(name, "name");
Ensure.ArgumentNotNullOrEmptyString(assignee, "assignee");
try
{
var response = await Connection.GetAsync<object>(ApiUrls.CheckAssignee(owner, name, assignee), null, null);
if (response.StatusCode != HttpStatusCode.NotFound && response.StatusCode != HttpStatusCode.NoContent)
{
throw new ApiException("Invalid Status Code returned. Expected a 204 or a 404", response.StatusCode);
}
return response.StatusCode == HttpStatusCode.NoContent;
}
catch (NotFoundException)
{
return false;
}
}
}
}

View File

@@ -7,8 +7,11 @@ namespace Octokit
{
public IssuesClient(IApiConnection apiConnection) : base(apiConnection)
{
Assignee = new AssigneesClient(apiConnection);
}
public IAssigneesClient Assignee { get; private set; }
/// <summary>
/// Gets a single Issue by number./// </summary>
/// <remarks>

View File

@@ -34,15 +34,16 @@ namespace Octokit
Ensure.ArgumentNotNull(connection, "connection");
Connection = connection;
Authorization = new AuthorizationsClient(new ApiConnection(connection));
Issue = new IssuesClient(new ApiConnection(connection));
var apiConnection = new ApiConnection(connection);
Authorization = new AuthorizationsClient(apiConnection);
Issue = new IssuesClient(apiConnection);
Miscellaneous = new MiscellaneousClient(connection);
Notification = new NotificationsClient(new ApiConnection(connection));
Organization = new OrganizationsClient(new ApiConnection(connection));
Repository = new RepositoriesClient(new ApiConnection(connection));
Release = new ReleasesClient(new ApiConnection(connection));
User = new UsersClient(new ApiConnection(connection));
SshKey = new SshKeysClient(new ApiConnection(connection));
Notification = new NotificationsClient(apiConnection);
Organization = new OrganizationsClient(apiConnection);
Repository = new RepositoriesClient(apiConnection);
Release = new ReleasesClient(apiConnection);
User = new UsersClient(apiConnection);
SshKey = new SshKeysClient(apiConnection);
}
/// <summary>

View File

@@ -188,6 +188,28 @@ namespace Octokit
return "/repos/{0}/{1}/issues/{2}".FormatUri(owner, name, number);
}
/// <summary>
/// Returns the <see cref="Uri"/> that returns all of the assignees to which issues may be assigned.
/// </summary>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <returns></returns>
public static Uri Assignees(string owner, string name)
{
return "/repos/{0}/{1}/assignees".FormatUri(owner, name);
}
/// <summary>
/// Returns the <see cref="Uri"/> that returns a 204 if the login belongs to an assignee of the repository.
/// Otherwire returns a 404.
/// </summary>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="login">The login for the user</param>
/// <returns></returns>
public static Uri CheckAssignee(string owner, string name, string login)
{
return "/repos/{0}/{1}/assignees/{2}".FormatUri(owner, name, login);
}
}
}

View File

@@ -38,7 +38,7 @@ namespace Octokit
/// <summary>
/// Gets the connection for making HTTP requests.
/// </summary>
protected IConnection Connection { get; private set; }
public IConnection Connection { get; private set; }
/// <summary>
/// Gets the API resource at the specified URI.

View File

@@ -11,6 +11,11 @@ namespace Octokit
/// </summary>
public interface IApiConnection
{
/// <summary>
/// The underlying connection.
/// </summary>
IConnection Connection { get; }
/// <summary>
/// Gets the API resource at the specified URI.
/// </summary>

View File

@@ -0,0 +1,25 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Octokit
{
public interface IAssigneesClient
{
/// <summary>
/// Gets all the available assignees (owner + collaborators) to which issues may be assigned.
/// </summary>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <returns></returns>
Task<IReadOnlyList<User>> GetForRepository(string owner, string name);
/// <summary>
/// Checks to see if a user is an assignee for a repository.
/// </summary>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="assignee">Username of the prospective assignee</param>
/// <returns></returns>
Task<bool> CheckAssignee(string owner, string name, string assignee);
}
}

View File

@@ -6,6 +6,8 @@ namespace Octokit
{
public interface IIssuesClient
{
IAssigneesClient Assignee { get; }
/// <summary>
/// Gets a single Issue by number.
/// </summary>

View File

@@ -81,8 +81,10 @@
<Compile Include="..\SolutionInfo.cs">
<Link>Properties\SolutionInfo.cs</Link>
</Compile>
<Compile Include="Clients\AssigneesClient.cs" />
<Compile Include="Clients\IssuesClient.cs" />
<Compile Include="Exceptions\NotFoundException.cs" />
<Compile Include="IAssigneesClient.cs" />
<Compile Include="IIssuesClient.cs" />
<Compile Include="Models\Issue.cs" />
<Compile Include="Models\IssueRequest.cs" />

View File

@@ -110,6 +110,7 @@
<Compile Include="Authentication\TokenAuthenticator.cs" />
<Compile Include="Clients\ApiClient.cs" />
<Compile Include="Clients\ApiPagination.cs" />
<Compile Include="Clients\AssigneesClient.cs" />
<Compile Include="Clients\AuthorizationsClient.cs" />
<Compile Include="Clients\IssuesClient.cs" />
<Compile Include="Clients\MiscellaneousClient.cs" />
@@ -146,6 +147,7 @@
<Compile Include="Http\RateLimit.cs" />
<Compile Include="Http\ReadOnlyPagedCollection.cs" />
<Compile Include="IApiPagination.cs" />
<Compile Include="IAssigneesClient.cs" />
<Compile Include="IAuthorizationsClient.cs" />
<Compile Include="IGitHubClient.cs" />
<Compile Include="IIssuesClient.cs" />