Address code review changes and add XML comments

This commit is contained in:
Haacked
2013-10-10 14:22:09 -07:00
parent 33ad79c0fe
commit ae41b81025
13 changed files with 393 additions and 132 deletions

View File

@@ -16,36 +16,60 @@ namespace Octokit.Reactive.Clients
_client = client; _client = client;
} }
/// <summary>
/// Get all <see cref="Authorization"/>s for the authenticated user. This method requires basic auth.
/// </summary>
/// <remarks>
/// See <a href="http://developer.github.com/v3/oauth/#list-your-authorizations">API documentation</a> for more
/// details.
/// </remarks>
/// <returns>An <see cref="Authorization"/></returns>
public IObservable<IReadOnlyList<Authorization>> GetAll() public IObservable<IReadOnlyList<Authorization>> GetAll()
{ {
return _client.GetAll().ToObservable(); return _client.GetAll().ToObservable();
} }
/// <summary>
/// Get a specific <see cref="Authorization"/> for the authenticated user. This method requires basic auth.
/// </summary>
/// <remarks>
/// See <a href="http://developer.github.com/v3/oauth/#get-a-single-authorization">API documentation</a> for
/// more details.
/// </remarks>
/// <param name="id">The id of the <see cref="Authorization"/></param>
/// <returns>An <see cref="Authorization"/></returns>
public IObservable<Authorization> Get(int id) public IObservable<Authorization> Get(int id)
{ {
return _client.Get(id).ToObservable(); return _client.Get(id).ToObservable();
} }
/// <summary> /// <summary>
/// This method will create a new authorization for the specified OAuth application, only if an authorization /// This method will create a new authorization for the specified OAuth application, only if an authorization
/// for that application doesnt already exist for the user. It returns the users token for the application /// for that application doesnt already exist for the user. It returns the users token for the application
/// if one exists. Otherwise, it creates one. /// if one exists. Otherwise, it creates one.
/// </summary> /// </summary>
/// <param name="clientId">Client ID for the OAuth application that is requesting the token.</param> /// <remarks>
/// See <a href="http://developer.github.com/v3/oauth/#get-or-create-an-authorization-for-a-specific-app">API
/// documentation</a> for more details.
/// </remarks>
/// <param name="clientId">Client ID for the OAuth application that is requesting the token</param>
/// <param name="clientSecret">The client secret</param> /// <param name="clientSecret">The client secret</param>
/// <param name="authorization">Definse the scopes and metadata for the token</param> /// <param name="newAuthorization">Defines the scopes and metadata for the token</param>
/// <exception cref="AuthorizationException">Thrown when the user does not have permission to make /// <exception cref="AuthorizationException">Thrown when the user does not have permission to make
/// this request. Check </exception> /// this request. Check </exception>
/// <exception cref="TwoFactorRequiredException">Thrown when the current account has two-factor
/// authentication enabled.</exception>
/// <returns></returns> /// <returns></returns>
public IObservable<Authorization> GetOrCreateApplicationAuthentication( public IObservable<Authorization> GetOrCreateApplicationAuthentication(
string clientId, string clientId,
string clientSecret, string clientSecret,
AuthorizationUpdate authorization) NewAuthorization newAuthorization)
{ {
Ensure.ArgumentNotNullOrEmptyString(clientId, "clientId"); Ensure.ArgumentNotNullOrEmptyString(clientId, "clientId");
Ensure.ArgumentNotNullOrEmptyString(clientSecret, "clientSecret"); Ensure.ArgumentNotNullOrEmptyString(clientSecret, "clientSecret");
Ensure.ArgumentNotNull(authorization, "authorization"); Ensure.ArgumentNotNull(newAuthorization, "authorization");
return _client.GetOrCreateApplicationAuthentication(clientId, clientSecret, authorization) return _client.GetOrCreateApplicationAuthentication(clientId, clientSecret, newAuthorization)
.ToObservable(); .ToObservable();
} }
@@ -54,45 +78,68 @@ namespace Octokit.Reactive.Clients
/// for that application doesnt already exist for the user. It returns the users token for the application /// for that application doesnt already exist for the user. It returns the users token for the application
/// if one exists. Otherwise, it creates one. /// if one exists. Otherwise, it creates one.
/// </summary> /// </summary>
/// <param name="clientId">Client ID for the OAuth application that is requesting the token.</param> /// <remarks>
/// See <a href="http://developer.github.com/v3/oauth/#get-or-create-an-authorization-for-a-specific-app">API
/// documentation</a> for more details.
/// </remarks>
/// <param name="clientId">Client ID for the OAuth application that is requesting the token</param>
/// <param name="clientSecret">The client secret</param> /// <param name="clientSecret">The client secret</param>
/// <param name="authorization">Defines the scopes and metadata for the token</param> /// <param name="newAuthorization">Defines the scopes and metadata for the token</param>
/// <param name="twoFactorAuthenticationCode"></param> /// <param name="twoFactorAuthenticationCode"></param>
/// <exception cref="AuthorizationException">Thrown when the user does not have permission to make /// <exception cref="AuthorizationException">Thrown when the user does not have permission to make
/// this request. Check </exception> /// this request. Check </exception>
/// <exception cref="TwoFactorChallengeFailedException">Thrown when the two-factor code is not /// <exception cref="TwoFactorChallengeFailedException">Thrown when the two-factor code is not
/// valid.</exception> /// valid.</exception>
/// <returns></returns> /// <returns></returns>
public IObservable<Authorization> GetOrCreateApplicationAuthentication( public IObservable<Authorization> GetOrCreateApplicationAuthentication(
string clientId, string clientId,
string clientSecret, string clientSecret,
AuthorizationUpdate authorization, NewAuthorization newAuthorization,
string twoFactorAuthenticationCode) string twoFactorAuthenticationCode)
{ {
Ensure.ArgumentNotNullOrEmptyString(clientId, "clientId"); Ensure.ArgumentNotNullOrEmptyString(clientId, "clientId");
Ensure.ArgumentNotNullOrEmptyString(clientSecret, "clientSecret"); Ensure.ArgumentNotNullOrEmptyString(clientSecret, "clientSecret");
Ensure.ArgumentNotNull(authorization, "authorization"); Ensure.ArgumentNotNull(newAuthorization, "authorization");
Ensure.ArgumentNotNullOrEmptyString(twoFactorAuthenticationCode, "twoFactorAuthenticationCode"); Ensure.ArgumentNotNullOrEmptyString(twoFactorAuthenticationCode, "twoFactorAuthenticationCode");
return _client.GetOrCreateApplicationAuthentication(clientId, clientSecret, authorization, twoFactorAuthenticationCode) return _client.GetOrCreateApplicationAuthentication(
clientId,
clientSecret,
newAuthorization,
twoFactorAuthenticationCode)
.ToObservable(); .ToObservable();
} }
public IObservable<Authorization> Update(int id, AuthorizationUpdate authorization) /// <summary>
/// Create a new <see cref="Authorization"/>.
/// </summary>
/// <param name="newAuthorization">Information about the new authorization to create</param>
/// <returns></returns>
public IObservable<Authorization> Create(NewAuthorization newAuthorization)
{ {
Ensure.ArgumentNotNull(authorization, "authorization"); Ensure.ArgumentNotNull(newAuthorization, "authorization");
return _client.Update(id, authorization).ToObservable(); return _client.Create(newAuthorization).ToObservable();
} }
public IObservable<Authorization> Create(AuthorizationUpdate authorization) /// <summary>
/// Update the <see cref="Authorization"/> specified by the id.
/// </summary>
/// <param name="id">The id of the <see cref="Authorization"/></param>
/// <param name="authorizationUpdate">The changes to make to the authorization</param>
/// <returns></returns>
public IObservable<Authorization> Update(int id, AuthorizationUpdate authorizationUpdate)
{ {
Ensure.ArgumentNotNull(authorization, "authorization"); Ensure.ArgumentNotNull(authorizationUpdate, "authorizationUpdate");
return _client.Create(authorization).ToObservable(); return _client.Update(id, authorizationUpdate).ToObservable();
} }
/// <summary>
/// Deletes an <see cref="Authorization"/>.
/// </summary>
/// <param name="id">The systemwide id of the authorization</param>
/// <returns></returns>
public IObservable<Unit> Delete(int id) public IObservable<Unit> Delete(int id)
{ {
return _client.Delete(id).ToObservable(); return _client.Delete(id).ToObservable();

View File

@@ -6,31 +6,53 @@ namespace Octokit
{ {
public static class AuthorizationExtensions public static class AuthorizationExtensions
{ {
/// <summary>
/// This method will create a new authorization for the specified OAuth application, only if an authorization
/// for that application doesnt already exist for the user. It returns the users token for the application
/// if one exists. Otherwise, it creates a new one.
/// </summary>
/// <remarks>
/// <para>
/// This method allows the caller to provide a callback which is used to retrieve the two-factor code from
/// the user. Typically the callback is used to show some user interface to the user.
/// </para>
/// <para>
/// See <a href="http://developer.github.com/v3/oauth/#list-your-authorizations">API documentation</a>
/// for more details.
/// </para>
/// </remarks>
/// <param name="authorizationsClient">The <see cref="IAuthorizationsClient" /> this method extends</param>
/// <param name="clientId">Client ID for the OAuth application that is requesting the token</param>
/// <param name="clientSecret">The client secret</param>
/// <param name="newAuthorization">Defines the scopes and metadata for the token</param>
/// <param name="twoFactorChallengeHandler">Callback used to retrieve the two-factor authentication code
/// from the user</param>
/// <returns></returns>
public static IObservable<Authorization> GetOrCreateApplicationAuthentication( public static IObservable<Authorization> GetOrCreateApplicationAuthentication(
this IObservableAuthorizationsClient authorizationsClient, this IObservableAuthorizationsClient authorizationsClient,
string clientId, string clientId,
string clientSecret, string clientSecret,
AuthorizationUpdate authorization, NewAuthorization newAuthorization,
Func<TwoFactorRequiredException, IObservable<TwoFactorChallengeResult>> twoFactorChallengeHandler Func<TwoFactorRequiredException, IObservable<TwoFactorChallengeResult>> twoFactorChallengeHandler
) )
{ {
Ensure.ArgumentNotNull(authorizationsClient, "authorizationsClient"); Ensure.ArgumentNotNull(authorizationsClient, "authorizationsClient");
Ensure.ArgumentNotNullOrEmptyString(clientId, "clientId"); Ensure.ArgumentNotNullOrEmptyString(clientId, "clientId");
Ensure.ArgumentNotNullOrEmptyString(clientSecret, "clientSecret"); Ensure.ArgumentNotNullOrEmptyString(clientSecret, "clientSecret");
Ensure.ArgumentNotNull(authorization, "authorization"); Ensure.ArgumentNotNull(newAuthorization, "authorization");
return authorizationsClient.GetOrCreateApplicationAuthentication(clientId, clientSecret, authorization) return authorizationsClient.GetOrCreateApplicationAuthentication(clientId, clientSecret, newAuthorization)
.Catch<Authorization, TwoFactorRequiredException>(exception => twoFactorChallengeHandler(exception) .Catch<Authorization, TwoFactorRequiredException>(exception => twoFactorChallengeHandler(exception)
.SelectMany(result => .SelectMany(result =>
result.ResendCodeRequested result.ResendCodeRequested
? authorizationsClient.GetOrCreateApplicationAuthentication( ? authorizationsClient.GetOrCreateApplicationAuthentication(
clientId, clientId,
clientSecret, clientSecret,
authorization, newAuthorization,
twoFactorChallengeHandler) twoFactorChallengeHandler)
: authorizationsClient.GetOrCreateApplicationAuthentication(clientId, : authorizationsClient.GetOrCreateApplicationAuthentication(clientId,
clientSecret, clientSecret,
authorization, newAuthorization,
result.AuthenticationCode))); result.AuthenticationCode)));
} }
} }

View File

@@ -7,9 +7,27 @@ namespace Octokit.Reactive
{ {
public interface IObservableAuthorizationsClient public interface IObservableAuthorizationsClient
{ {
/// <summary>
/// Get all <see cref="Authorization"/>s for the authenticated user. This method requires basic auth.
/// </summary>
/// <remarks>
/// See <a href="http://developer.github.com/v3/oauth/#list-your-authorizations">API documentation</a> for more
/// details.
/// </remarks>
/// <returns>An <see cref="Authorization"/></returns>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
Justification = "It's an API call, so it's not a property.")] Justification = "It's an API call, so it's not a property.")]
IObservable<IReadOnlyList<Authorization>> GetAll(); IObservable<IReadOnlyList<Authorization>> GetAll();
/// <summary>
/// Get a specific <see cref="Authorization"/> for the authenticated user. This method requires basic auth.
/// </summary>
/// <remarks>
/// See <a href="http://developer.github.com/v3/oauth/#get-a-single-authorization">API documentation</a> for
/// more details.
/// </remarks>
/// <param name="id">The id of the <see cref="Authorization"/></param>
/// <returns>An <see cref="Authorization"/></returns>
[SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get", [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get",
Justification = "It's fiiiine. It's fine. Trust us.")] Justification = "It's fiiiine. It's fine. Trust us.")]
IObservable<Authorization> Get(int id); IObservable<Authorization> Get(int id);
@@ -19,9 +37,13 @@ namespace Octokit.Reactive
/// for that application doesnt already exist for the user. It returns the users token for the application /// for that application doesnt already exist for the user. It returns the users token for the application
/// if one exists. Otherwise, it creates one. /// if one exists. Otherwise, it creates one.
/// </summary> /// </summary>
/// <param name="clientId">Client ID for the OAuth application that is requesting the token.</param> /// <remarks>
/// See <a href="http://developer.github.com/v3/oauth/#get-or-create-an-authorization-for-a-specific-app">API
/// documentation</a> for more details.
/// </remarks>
/// <param name="clientId">Client ID for the OAuth application that is requesting the token</param>
/// <param name="clientSecret">The client secret</param> /// <param name="clientSecret">The client secret</param>
/// <param name="authorization">Defines the scopes and metadata for the token</param> /// <param name="newAuthorization">Defines the scopes and metadata for the token</param>
/// <exception cref="AuthorizationException">Thrown when the user does not have permission to make /// <exception cref="AuthorizationException">Thrown when the user does not have permission to make
/// this request. Check </exception> /// this request. Check </exception>
/// <exception cref="TwoFactorRequiredException">Thrown when the current account has two-factor /// <exception cref="TwoFactorRequiredException">Thrown when the current account has two-factor
@@ -30,16 +52,20 @@ namespace Octokit.Reactive
IObservable<Authorization> GetOrCreateApplicationAuthentication( IObservable<Authorization> GetOrCreateApplicationAuthentication(
string clientId, string clientId,
string clientSecret, string clientSecret,
AuthorizationUpdate authorization); NewAuthorization newAuthorization);
/// <summary> /// <summary>
/// This method will create a new authorization for the specified OAuth application, only if an authorization /// This method will create a new authorization for the specified OAuth application, only if an authorization
/// for that application doesnt already exist for the user. It returns the users token for the application /// for that application doesnt already exist for the user. It returns the users token for the application
/// if one exists. Otherwise, it creates one. /// if one exists. Otherwise, it creates one.
/// </summary> /// </summary>
/// <param name="clientId">Client ID for the OAuth application that is requesting the token.</param> /// <remarks>
/// See <a href="http://developer.github.com/v3/oauth/#get-or-create-an-authorization-for-a-specific-app">API
/// documentation</a> for more details.
/// </remarks>
/// <param name="clientId">Client ID for the OAuth application that is requesting the token</param>
/// <param name="clientSecret">The client secret</param> /// <param name="clientSecret">The client secret</param>
/// <param name="authorization">Defines the scopes and metadata for the token</param> /// <param name="newAuthorization">Defines the scopes and metadata for the token</param>
/// <param name="twoFactorAuthenticationCode"></param> /// <param name="twoFactorAuthenticationCode"></param>
/// <exception cref="AuthorizationException">Thrown when the user does not have permission to make /// <exception cref="AuthorizationException">Thrown when the user does not have permission to make
/// this request. Check </exception> /// this request. Check </exception>
@@ -49,10 +75,29 @@ namespace Octokit.Reactive
IObservable<Authorization> GetOrCreateApplicationAuthentication( IObservable<Authorization> GetOrCreateApplicationAuthentication(
string clientId, string clientId,
string clientSecret, string clientSecret,
AuthorizationUpdate authorization, NewAuthorization newAuthorization,
string twoFactorAuthenticationCode); string twoFactorAuthenticationCode);
IObservable<Authorization> Update(int id, AuthorizationUpdate authorization);
IObservable<Authorization> Create(AuthorizationUpdate authorization); /// <summary>
/// Create a new <see cref="Authorization"/>.
/// </summary>
/// <param name="newAuthorization">Information about the new authorization to create</param>
/// <returns></returns>
IObservable<Authorization> Create(NewAuthorization newAuthorization);
/// <summary>
/// Update the <see cref="Authorization"/> specified by the id.
/// </summary>
/// <param name="id">The id of the <see cref="Authorization"/></param>
/// <param name="authorizationUpdate">The changes to make to the authorization</param>
/// <returns></returns>
IObservable<Authorization> Update(int id, AuthorizationUpdate authorizationUpdate);
/// <summary>
/// Deletes an <see cref="Authorization"/>.
/// </summary>
/// <param name="id">The systemwide id of the authorization</param>
/// <returns></returns>
IObservable<Unit> Delete(int id); IObservable<Unit> Delete(int id);
} }
} }

View File

@@ -73,10 +73,10 @@ namespace Octokit.Tests.Clients
var client = Substitute.For<IApiConnection<Authorization>>(); var client = Substitute.For<IApiConnection<Authorization>>();
var authEndpoint = new AuthorizationsClient(client); var authEndpoint = new AuthorizationsClient(client);
authEndpoint.Create(new AuthorizationUpdate()); authEndpoint.Create(new NewAuthorization());
client.Received().Create(Arg.Is<Uri>(u => u.ToString() == "/authorizations") client.Received().Create(Arg.Is<Uri>(u => u.ToString() == "/authorizations")
, Args.AuthorizationUpdate); , Args.NewAuthorization);
} }
} }
@@ -99,7 +99,7 @@ namespace Octokit.Tests.Clients
[Fact] [Fact]
public void GetsOrCreatesAuthenticationAtCorrectUrl() public void GetsOrCreatesAuthenticationAtCorrectUrl()
{ {
var data = new AuthorizationUpdate(); var data = new NewAuthorization();
var client = Substitute.For<IApiConnection<Authorization>>(); var client = Substitute.For<IApiConnection<Authorization>>();
var authEndpoint = new AuthorizationsClient(client); var authEndpoint = new AuthorizationsClient(client);
@@ -112,7 +112,7 @@ namespace Octokit.Tests.Clients
[Fact] [Fact]
public async Task WrapsTwoFactorFailureWithTwoFactorException() public async Task WrapsTwoFactorFailureWithTwoFactorException()
{ {
var data = new AuthorizationUpdate(); var data = new NewAuthorization();
var client = Substitute.For<IApiConnection<Authorization>>(); var client = Substitute.For<IApiConnection<Authorization>>();
client.GetOrCreate(Args.Uri, Args.Object, Args.String).Returns(_ => {throw new AuthorizationException();}); client.GetOrCreate(Args.Uri, Args.Object, Args.String).Returns(_ => {throw new AuthorizationException();});
var authEndpoint = new AuthorizationsClient(client); var authEndpoint = new AuthorizationsClient(client);
@@ -125,13 +125,13 @@ namespace Octokit.Tests.Clients
public async Task UsesCallbackToRetrieveTwoFactorCode() public async Task UsesCallbackToRetrieveTwoFactorCode()
{ {
var twoFactorChallengeResult = new TwoFactorChallengeResult("two-factor-code"); var twoFactorChallengeResult = new TwoFactorChallengeResult("two-factor-code");
var data = new AuthorizationUpdate { Note = "note" }; var data = new NewAuthorization { Note = "note" };
var client = Substitute.For<IAuthorizationsClient>(); var client = Substitute.For<IAuthorizationsClient>();
client.GetOrCreateApplicationAuthentication("clientId", "secret", Arg.Any<AuthorizationUpdate>()) client.GetOrCreateApplicationAuthentication("clientId", "secret", Arg.Any<NewAuthorization>())
.Returns(_ => {throw new TwoFactorRequiredException();}); .Returns(_ => {throw new TwoFactorRequiredException();});
client.GetOrCreateApplicationAuthentication("clientId", client.GetOrCreateApplicationAuthentication("clientId",
"secret", "secret",
Arg.Any<AuthorizationUpdate>(), Arg.Any<NewAuthorization>(),
"two-factor-code") "two-factor-code")
.Returns(Task.Factory.StartNew(() => new Authorization {Token = "xyz"})); .Returns(Task.Factory.StartNew(() => new Authorization {Token = "xyz"}));
@@ -142,10 +142,10 @@ namespace Octokit.Tests.Clients
client.Received().GetOrCreateApplicationAuthentication("clientId", client.Received().GetOrCreateApplicationAuthentication("clientId",
"secret", "secret",
Arg.Is<AuthorizationUpdate>(u => u.Note == "note")); Arg.Is<NewAuthorization>(u => u.Note == "note"));
client.Received().GetOrCreateApplicationAuthentication("clientId", client.Received().GetOrCreateApplicationAuthentication("clientId",
"secret", "secret",
Arg.Any<AuthorizationUpdate>(), "two-factor-code"); Arg.Any<NewAuthorization>(), "two-factor-code");
Assert.Equal("xyz", result.Token); Assert.Equal("xyz", result.Token);
} }
@@ -157,13 +157,13 @@ namespace Octokit.Tests.Clients
TwoFactorChallengeResult.RequestResendCode, TwoFactorChallengeResult.RequestResendCode,
new TwoFactorChallengeResult("two-factor-code") new TwoFactorChallengeResult("two-factor-code")
}); });
var data = new AuthorizationUpdate(); var data = new NewAuthorization();
var client = Substitute.For<IAuthorizationsClient>(); var client = Substitute.For<IAuthorizationsClient>();
client.GetOrCreateApplicationAuthentication("clientId", "secret", Arg.Any<AuthorizationUpdate>()) client.GetOrCreateApplicationAuthentication("clientId", "secret", Arg.Any<NewAuthorization>())
.Returns(_ => { throw new TwoFactorRequiredException(); }); .Returns(_ => { throw new TwoFactorRequiredException(); });
client.GetOrCreateApplicationAuthentication("clientId", client.GetOrCreateApplicationAuthentication("clientId",
"secret", "secret",
Arg.Any<AuthorizationUpdate>(), Arg.Any<NewAuthorization>(),
"two-factor-code") "two-factor-code")
.Returns(Task.Factory.StartNew(() => new Authorization { Token = "xyz" })); .Returns(Task.Factory.StartNew(() => new Authorization { Token = "xyz" }));
@@ -174,12 +174,47 @@ namespace Octokit.Tests.Clients
client.Received().GetOrCreateApplicationAuthentication("clientId", client.Received().GetOrCreateApplicationAuthentication("clientId",
"secret", "secret",
Arg.Any<AuthorizationUpdate>()); Arg.Any<NewAuthorization>());
client.Received().GetOrCreateApplicationAuthentication("clientId", client.Received().GetOrCreateApplicationAuthentication("clientId",
"secret", "secret",
Arg.Any<AuthorizationUpdate>(), "two-factor-code"); Arg.Any<NewAuthorization>(), "two-factor-code");
Assert.Equal("xyz", result.Token); Assert.Equal("xyz", result.Token);
} }
[Fact]
public async Task CallsCallbackAgainWhenUserSubmitsBadCode()
{
var challengeResults = new Queue<TwoFactorChallengeResult>(new[]
{
TwoFactorChallengeResult.RequestResendCode,
new TwoFactorChallengeResult("wrong-code"),
});
var data = new NewAuthorization();
var client = Substitute.For<IAuthorizationsClient>();
client.GetOrCreateApplicationAuthentication("clientId", "secret", Arg.Any<NewAuthorization>())
.Returns(_ => { throw new TwoFactorRequiredException(); });
client.GetOrCreateApplicationAuthentication("clientId",
"secret",
Arg.Any<NewAuthorization>(),
"wrong-code")
.Returns(_ => { throw new TwoFactorChallengeFailedException(); });
var exception = AssertEx.Throws<TwoFactorChallengeFailedException>(async () =>
await client.GetOrCreateApplicationAuthentication(
"clientId",
"secret",
data,
e => Task.Factory.StartNew(() => challengeResults.Dequeue())));
Assert.NotNull(exception);
client.Received().GetOrCreateApplicationAuthentication("clientId",
"secret",
Arg.Any<NewAuthorization>());
client.Received().GetOrCreateApplicationAuthentication("clientId",
"secret",
Arg.Any<NewAuthorization>(),
"wrong-code");
}
} }
} }
} }

View File

@@ -11,6 +11,11 @@ namespace Octokit.Tests
get { return Arg.Any<AuthorizationUpdate>(); } get { return Arg.Any<AuthorizationUpdate>(); }
} }
public static NewAuthorization NewAuthorization
{
get { return Arg.Any<NewAuthorization>(); }
}
public static Uri Uri public static Uri Uri
{ {
get { return Arg.Any<Uri>(); } get { return Arg.Any<Uri>(); }

View File

@@ -2,15 +2,19 @@
#if NET_45 #if NET_45
using System.Collections.Generic; using System.Collections.Generic;
#endif #endif
using System.Collections;
using System.Threading.Tasks; using System.Threading.Tasks;
using Octokit.Internal;
namespace Octokit namespace Octokit
{ {
/// <summary>
/// Client for the OAuth2 API.
/// </summary>
/// <remarks>
/// See <a href="http://developer.github.com/v3/oauth/">OAuth API documentation</a> for more details.
/// </remarks>
public class AuthorizationsClient : ApiClient<Authorization>, IAuthorizationsClient public class AuthorizationsClient : ApiClient<Authorization>, IAuthorizationsClient
{ {
static readonly Uri authorizationsEndpoint = new Uri("/authorizations", UriKind.Relative); static readonly Uri _authorizationsEndpoint = new Uri("/authorizations", UriKind.Relative);
public AuthorizationsClient(IApiConnection<Authorization> client) : base(client) public AuthorizationsClient(IApiConnection<Authorization> client) : base(client)
{ {
@@ -19,16 +23,24 @@ namespace Octokit
/// <summary> /// <summary>
/// Get all <see cref="Authorization"/>s for the authenticated user. This method requires basic auth. /// Get all <see cref="Authorization"/>s for the authenticated user. This method requires basic auth.
/// </summary> /// </summary>
/// <remarks>
/// See <a href="http://developer.github.com/v3/oauth/#list-your-authorizations">API documentation</a> for more
/// details.
/// </remarks>
/// <returns>An <see cref="Authorization"/></returns> /// <returns>An <see cref="Authorization"/></returns>
public async Task<IReadOnlyList<Authorization>> GetAll() public async Task<IReadOnlyList<Authorization>> GetAll()
{ {
return await Client.GetAll(authorizationsEndpoint); return await Client.GetAll(_authorizationsEndpoint);
} }
/// <summary> /// <summary>
/// Get a specific <see cref="Authorization"/> for the authenticated user. This method requires basic auth. /// Get a specific <see cref="Authorization"/> for the authenticated user. This method requires basic auth.
/// </summary> /// </summary>
/// <param name="id">The id of the <see cref="Authorization"/>.</param> /// <remarks>
/// See <a href="http://developer.github.com/v3/oauth/#get-a-single-authorization">API documentation</a> for
/// more details.
/// </remarks>
/// <param name="id">The id of the <see cref="Authorization"/></param>
/// <returns>An <see cref="Authorization"/></returns> /// <returns>An <see cref="Authorization"/></returns>
public async Task<Authorization> Get(int id) public async Task<Authorization> Get(int id)
{ {
@@ -41,49 +53,75 @@ namespace Octokit
/// for that application doesnt already exist for the user. It returns the users token for the application /// for that application doesnt already exist for the user. It returns the users token for the application
/// if one exists. Otherwise, it creates one. /// if one exists. Otherwise, it creates one.
/// </summary> /// </summary>
/// <param name="clientId">Client ID for the OAuth application that is requesting the token.</param> /// <remarks>
/// See <a href="http://developer.github.com/v3/oauth/#get-or-create-an-authorization-for-a-specific-app">API
/// documentation</a> for more details.
/// </remarks>
/// <param name="clientId">Client ID for the OAuth application that is requesting the token</param>
/// <param name="clientSecret">The client secret</param> /// <param name="clientSecret">The client secret</param>
/// <param name="authorization">Definse the scopes and metadata for the token</param> /// <param name="newAuthorization">Defines the scopes and metadata for the token</param>
/// <exception cref="AuthorizationException">Thrown when the user does not have permission to make
/// this request. Check </exception>
/// <exception cref="TwoFactorRequiredException">Thrown when the current account has two-factor
/// authentication enabled.</exception>
/// <returns></returns> /// <returns></returns>
public async Task<Authorization> GetOrCreateApplicationAuthentication( public async Task<Authorization> GetOrCreateApplicationAuthentication(
string clientId, string clientId,
string clientSecret, string clientSecret,
AuthorizationUpdate authorization) NewAuthorization newAuthorization)
{ {
Ensure.ArgumentNotNullOrEmptyString(clientId, "clientId"); Ensure.ArgumentNotNullOrEmptyString(clientId, "clientId");
Ensure.ArgumentNotNullOrEmptyString(clientSecret, "clientSecret"); Ensure.ArgumentNotNullOrEmptyString(clientSecret, "clientSecret");
Ensure.ArgumentNotNull(authorization, "authorization"); Ensure.ArgumentNotNull(newAuthorization, "authorization");
var endpoint = "/authorizations/clients/{0}".FormatUri(clientId); var endpoint = "/authorizations/clients/{0}".FormatUri(clientId);
var requestData = new var requestData = new
{ {
client_secret = clientSecret, client_secret = clientSecret,
scopes = authorization.Scopes, scopes = newAuthorization.Scopes,
note = authorization.Note, note = newAuthorization.Note,
note_url = authorization.NoteUrl note_url = newAuthorization.NoteUrl
}; };
return await Client.GetOrCreate(endpoint, requestData); return await Client.GetOrCreate(endpoint, requestData);
} }
/// <summary>
/// This method will create a new authorization for the specified OAuth application, only if an authorization
/// for that application doesnt already exist for the user. It returns the users token for the application
/// if one exists. Otherwise, it creates one.
/// </summary>
/// <remarks>
/// See <a href="http://developer.github.com/v3/oauth/#get-or-create-an-authorization-for-a-specific-app">API
/// documentation</a> for more details.
/// </remarks>
/// <param name="clientId">Client ID for the OAuth application that is requesting the token</param>
/// <param name="clientSecret">The client secret</param>
/// <param name="newAuthorization">Defines the scopes and metadata for the token</param>
/// <param name="twoFactorAuthenticationCode"></param>
/// <exception cref="AuthorizationException">Thrown when the user does not have permission to make
/// this request. Check </exception>
/// <exception cref="TwoFactorChallengeFailedException">Thrown when the two-factor code is not
/// valid.</exception>
/// <returns></returns>
public async Task<Authorization> GetOrCreateApplicationAuthentication( public async Task<Authorization> GetOrCreateApplicationAuthentication(
string clientId, string clientId,
string clientSecret, string clientSecret,
AuthorizationUpdate authorization, NewAuthorization newAuthorization,
string twoFactorAuthenticationCode) string twoFactorAuthenticationCode)
{ {
Ensure.ArgumentNotNullOrEmptyString(clientId, "clientId"); Ensure.ArgumentNotNullOrEmptyString(clientId, "clientId");
Ensure.ArgumentNotNullOrEmptyString(clientSecret, "clientSecret"); Ensure.ArgumentNotNullOrEmptyString(clientSecret, "clientSecret");
Ensure.ArgumentNotNull(authorization, "authorization"); Ensure.ArgumentNotNull(newAuthorization, "authorization");
Ensure.ArgumentNotNullOrEmptyString(twoFactorAuthenticationCode, "twoFactorAuthenticationCode"); Ensure.ArgumentNotNullOrEmptyString(twoFactorAuthenticationCode, "twoFactorAuthenticationCode");
var endpoint = "/authorizations/clients/{0}".FormatUri(clientId); var endpoint = "/authorizations/clients/{0}".FormatUri(clientId);
var requestData = new var requestData = new
{ {
client_secret = clientSecret, client_secret = clientSecret,
scopes = authorization.Scopes, scopes = newAuthorization.Scopes,
note = authorization.Note, note = newAuthorization.Note,
note_url = authorization.NoteUrl note_url = newAuthorization.NoteUrl
}; };
try try
@@ -102,27 +140,27 @@ namespace Octokit
/// <summary> /// <summary>
/// Update the specified <see cref="Authorization"/>. /// Update the specified <see cref="Authorization"/>.
/// </summary> /// </summary>
/// <param name="id">The id of the <see cref="Authorization"/>.</param> /// <param name="id">The id of the <see cref="Authorization"/></param>
/// <param name="authorization"></param> /// <param name="authorizationUpdate">The changes to make to the authorization</param>
/// <returns></returns> /// <returns></returns>
public async Task<Authorization> Update(int id, AuthorizationUpdate authorization) public async Task<Authorization> Update(int id, AuthorizationUpdate authorizationUpdate)
{ {
Ensure.ArgumentNotNull(authorization, "authorization"); Ensure.ArgumentNotNull(authorizationUpdate, "authorizationUpdate");
var endpoint = "/authorizations/{0}".FormatUri(id); var endpoint = "/authorizations/{0}".FormatUri(id);
return await Client.Update(endpoint, authorization); return await Client.Update(endpoint, authorizationUpdate);
} }
/// <summary> /// <summary>
/// Create a new <see cref="Authorization"/>. /// Create a new <see cref="Authorization"/>.
/// </summary> /// </summary>
/// <param name="authorization"></param> /// <param name="newAuthorization"></param>
/// <returns></returns> /// <returns></returns>
public async Task<Authorization> Create(AuthorizationUpdate authorization) public async Task<Authorization> Create(NewAuthorization newAuthorization)
{ {
Ensure.ArgumentNotNull(authorization, "authorization"); Ensure.ArgumentNotNull(newAuthorization, "newAuthorization");
return await Client.Create(authorizationsEndpoint, authorization); return await Client.Create(_authorizationsEndpoint, newAuthorization);
} }
/// <summary> /// <summary>

View File

@@ -34,42 +34,3 @@ namespace Octokit
#endif #endif
} }
} }
/*
[Fact]
public void CreatesGitHubErrorFromJsonResponse()
{
var exception = new ApiUnauthorizedWebException("{\"message\":\"Bad credentials.\"}");
exception.ApiUnauthorizedError.Message.ShouldEqual("Bad credentials.");
exception.ApiUnauthorizedError.Errors.ShouldBeNull();
}
[Theory]
[InlineData("")]
[InlineData(null)]
[InlineData("{{{{{")]
public void CreatesGitHubErrorIfResponseMessageIsNotValidJson(string responseContent)
{
var exception = new ApiUnauthorizedWebException(responseContent);
exception.ApiUnauthorizedError.Message.ShouldEqual(responseContent);
Assert.False(exception.RequiresSecondFactor);
}
[Fact]
public void CanPopulateObjectFromSerializedData()
{
var exception = new ApiUnauthorizedWebException("{message:\"Bad credentials.\"}");
using (var stream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, exception);
stream.Position = 0;
var deserialized = (ApiUnauthorizedWebException)formatter.Deserialize(stream);
deserialized.ApiUnauthorizedError.Message.ShouldEqual("Bad credentials.");
exception.ApiUnauthorizedError.Errors.ShouldBeNull();
}
}
*/

View File

@@ -5,19 +5,40 @@ namespace Octokit
{ {
public static class AuthorizationExtensions public static class AuthorizationExtensions
{ {
/// <summary>
/// This method will create a new authorization for the specified OAuth application, only if an authorization
/// for that application doesnt already exist for the user. It returns the users token for the application
/// if one exists. Otherwise, it creates a new one.
/// </summary>
/// <remarks>
/// <para>
/// This method allows the caller to provide a callback which is used to retrieve the two-factor code from
/// the user. Typically the callback is used to show some user interface to the user.
/// </para>
/// <para>
/// See <a href="http://developer.github.com/v3/oauth/#list-your-authorizations">API documentation</a>
/// for more details.
/// </para>
/// </remarks>
/// <param name="authorizationsClient">The <see cref="IAuthorizationsClient" /> this method extends</param>
/// <param name="clientId">Client ID for the OAuth application that is requesting the token</param>
/// <param name="clientSecret">The client secret</param>
/// <param name="newAuthorization">Defines the scopes and metadata for the token</param>
/// <param name="twoFactorChallengeHandler">Callback used to retrieve the two-factor authentication code
/// from the user</param>
/// <returns></returns>
public static async Task<Authorization> GetOrCreateApplicationAuthentication( public static async Task<Authorization> GetOrCreateApplicationAuthentication(
this IAuthorizationsClient authorizationsClient, this IAuthorizationsClient authorizationsClient,
string clientId, string clientId,
string clientSecret, string clientSecret,
AuthorizationUpdate authorization, NewAuthorization newAuthorization,
Func<TwoFactorRequiredException, Task<TwoFactorChallengeResult>> twoFactorChallengeHandler Func<TwoFactorRequiredException, Task<TwoFactorChallengeResult>> twoFactorChallengeHandler
) )
{ {
TwoFactorRequiredException twoFactorException = null; TwoFactorRequiredException twoFactorException = null;
try try
{ {
return await authorizationsClient.GetOrCreateApplicationAuthentication(clientId, clientSecret, authorization); return await authorizationsClient.GetOrCreateApplicationAuthentication(clientId, clientSecret, newAuthorization);
} }
catch (TwoFactorRequiredException exception) catch (TwoFactorRequiredException exception)
{ {
@@ -29,13 +50,13 @@ namespace Octokit
? authorizationsClient.GetOrCreateApplicationAuthentication( ? authorizationsClient.GetOrCreateApplicationAuthentication(
clientId, clientId,
clientSecret, clientSecret,
authorization, newAuthorization,
twoFactorChallengeHandler) twoFactorChallengeHandler)
: authorizationsClient.GetOrCreateApplicationAuthentication(clientId, : authorizationsClient.GetOrCreateApplicationAuthentication(
clientId,
clientSecret, clientSecret,
authorization, newAuthorization,
twoFactorChallengeResult.AuthenticationCode)); twoFactorChallengeResult.AuthenticationCode));
} }
} }
} }

View File

@@ -4,27 +4,51 @@ using System.Threading.Tasks;
namespace Octokit namespace Octokit
{ {
/// <summary>
/// Interface for the client of the OAuth2 API.
/// </summary>
/// <remarks>
/// See <a href="http://developer.github.com/v3/oauth/">OAuth API documentation</a> for more details.
/// </remarks>
public interface IAuthorizationsClient public interface IAuthorizationsClient
{ {
/// <summary>
/// Get all <see cref="Authorization"/>s for the authenticated user. This method requires basic auth.
/// </summary>
/// <remarks>
/// See <a href="http://developer.github.com/v3/oauth/#list-your-authorizations">API documentation</a> for more
/// details.
/// </remarks>
/// <returns>An <see cref="Authorization"/></returns>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
Justification = "It's an API call, so it's not a property.")] Justification = "It's an API call, so it's not a property.")]
Task<IReadOnlyList<Authorization>> GetAll(); Task<IReadOnlyList<Authorization>> GetAll();
/// <summary> /// <summary>
/// Get a specific <see cref="Authorization"/> for the authenticated user. This method requires basic auth. /// Get a specific <see cref="Authorization"/> for the authenticated user. This method requires basic auth.
/// </summary> /// </summary>
/// <param name="id">The id of the <see cref="Authorization"/>.</param> /// <remarks>
/// See <a href="http://developer.github.com/v3/oauth/#get-a-single-authorization">API documentation</a> for
/// more details.
/// </remarks>
/// <param name="id">The id of the <see cref="Authorization"/></param>
/// <returns>An <see cref="Authorization"/></returns> /// <returns>An <see cref="Authorization"/></returns>
[SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get", [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get",
Justification = "It's fiiiine. It's fine. Trust us.")] Justification = "It's fiiiine. It's fine. Trust us.")]
Task<Authorization> Get(int id); Task<Authorization> Get(int id);
/// <summary> /// <summary>
/// This method will create a new authorization for the specified OAuth application, only if an authorization /// This method will create a new authorization for the specified OAuth application, only if an authorization
/// for that application doesnt already exist for the user. It returns the users token for the application /// for that application doesnt already exist for the user. It returns the users token for the application
/// if one exists. Otherwise, it creates one. /// if one exists. Otherwise, it creates one.
/// </summary> /// </summary>
/// <param name="clientId">Client ID for the OAuth application that is requesting the token.</param> /// <remarks>
/// See <a href="http://developer.github.com/v3/oauth/#get-or-create-an-authorization-for-a-specific-app">API
/// documentation</a> for more details.
/// </remarks>
/// <param name="clientId">Client ID for the OAuth application that is requesting the token</param>
/// <param name="clientSecret">The client secret</param> /// <param name="clientSecret">The client secret</param>
/// <param name="authorization">Defines the scopes and metadata for the token</param> /// <param name="newAuthorization">Defines the scopes and metadata for the token</param>
/// <exception cref="AuthorizationException">Thrown when the user does not have permission to make /// <exception cref="AuthorizationException">Thrown when the user does not have permission to make
/// this request. Check </exception> /// this request. Check </exception>
/// <exception cref="TwoFactorRequiredException">Thrown when the current account has two-factor /// <exception cref="TwoFactorRequiredException">Thrown when the current account has two-factor
@@ -33,15 +57,20 @@ namespace Octokit
Task<Authorization> GetOrCreateApplicationAuthentication( Task<Authorization> GetOrCreateApplicationAuthentication(
string clientId, string clientId,
string clientSecret, string clientSecret,
AuthorizationUpdate authorization); NewAuthorization newAuthorization);
/// <summary> /// <summary>
/// This method will create a new authorization for the specified OAuth application, only if an authorization /// This method will create a new authorization for the specified OAuth application, only if an authorization
/// for that application doesnt already exist for the user. It returns the users token for the application /// for that application doesnt already exist for the user. It returns the users token for the application
/// if one exists. Otherwise, it creates one. /// if one exists. Otherwise, it creates one.
/// </summary> /// </summary>
/// <param name="clientId">Client ID for the OAuth application that is requesting the token.</param> /// <remarks>
/// See <a href="http://developer.github.com/v3/oauth/#get-or-create-an-authorization-for-a-specific-app">API
/// documentation</a> for more details.
/// </remarks>
/// <param name="clientId">Client ID for the OAuth application that is requesting the token</param>
/// <param name="clientSecret">The client secret</param> /// <param name="clientSecret">The client secret</param>
/// <param name="authorization">Defines the scopes and metadata for the token</param> /// <param name="newAuthorization">Defines the scopes and metadata for the token</param>
/// <param name="twoFactorAuthenticationCode"></param> /// <param name="twoFactorAuthenticationCode"></param>
/// <exception cref="AuthorizationException">Thrown when the user does not have permission to make /// <exception cref="AuthorizationException">Thrown when the user does not have permission to make
/// this request. Check </exception> /// this request. Check </exception>
@@ -51,10 +80,29 @@ namespace Octokit
Task<Authorization> GetOrCreateApplicationAuthentication( Task<Authorization> GetOrCreateApplicationAuthentication(
string clientId, string clientId,
string clientSecret, string clientSecret,
AuthorizationUpdate authorization, NewAuthorization newAuthorization,
string twoFactorAuthenticationCode); string twoFactorAuthenticationCode);
Task<Authorization> Update(int id, AuthorizationUpdate authorization);
Task<Authorization> Create(AuthorizationUpdate authorization); /// <summary>
/// Create a new <see cref="Authorization"/>.
/// </summary>
/// <param name="newAuthorization">Information about the new authorization to create</param>
/// <returns></returns>
Task<Authorization> Create(NewAuthorization newAuthorization);
/// <summary>
/// Update the <see cref="Authorization"/> specified by the id.
/// </summary>
/// <param name="id">The id of the <see cref="Authorization"/></param>
/// <param name="authorizationUpdate">The changes to make to the authorization</param>
/// <returns></returns>
Task<Authorization> Update(int id, AuthorizationUpdate authorizationUpdate);
/// <summary>
/// Deletes an <see cref="Authorization"/>.
/// </summary>
/// <param name="id">The systemwide id of the authorization</param>
/// <returns></returns>
Task Delete(int id); Task Delete(int id);
} }
} }

View File

@@ -1,22 +1,34 @@
using System.Diagnostics.CodeAnalysis; using System.Collections.Generic;
namespace Octokit namespace Octokit
{ {
/// <summary>
/// Used to create a new authorization.
/// </summary>
public class AuthorizationUpdate public class AuthorizationUpdate
{ {
/// <summary> /// <summary>
/// Replace scopes with this list. /// Replaces the authorization scopes with this list.
/// </summary> /// </summary>
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Special type of model object that only updates none-null fields.")] public IEnumerable<string> Scopes { get; set; }
public string[] Scopes { get; set; }
/// <summary> /// <summary>
/// Notes about this particular <see cref="Authorization"/>. /// A list of scopes to add to this authorization.
/// </summary>
public IEnumerable<string> AddScopes { get; set; }
/// <summary>
/// A list of scopes to remove from this authorization.
/// </summary>
public IEnumerable<string> RemoveScopes { get; set; }
/// <summary>
/// An optional note to remind you what the OAuth token is for.
/// </summary> /// </summary>
public string Note { get; set; } public string Note { get; set; }
/// <summary> /// <summary>
/// A url for more information about notes. // An optional URL to remind you what app the OAuth token is for.
/// </summary> /// </summary>
public string NoteUrl { get; set; } public string NoteUrl { get; set; }
} }

View File

@@ -0,0 +1,25 @@
using System.Collections.Generic;
namespace Octokit
{
/// <summary>
/// Used to create a new authorization.
/// </summary>
public class NewAuthorization
{
/// <summary>
/// Replaces the authorization scopes with this list.
/// </summary>
public IEnumerable<string> Scopes { get; set; }
/// <summary>
/// An optional note to remind you what the OAuth token is for.
/// </summary>
public string Note { get; set; }
/// <summary>
// An optional URL to remind you what app the OAuth token is for.
/// </summary>
public string NoteUrl { get; set; }
}
}

View File

@@ -151,6 +151,7 @@
<Compile Include="ModelExtensions.cs" /> <Compile Include="ModelExtensions.cs" />
<Compile Include="Models\Authorization.cs" /> <Compile Include="Models\Authorization.cs" />
<Compile Include="Models\AuthorizationUpdate.cs" /> <Compile Include="Models\AuthorizationUpdate.cs" />
<Compile Include="Models\NewAuthorization.cs" />
<Compile Include="Models\EmailAddress.cs" /> <Compile Include="Models\EmailAddress.cs" />
<Compile Include="Models\Organization.cs" /> <Compile Include="Models\Organization.cs" />
<Compile Include="Models\Plan.cs" /> <Compile Include="Models\Plan.cs" />

View File

@@ -170,6 +170,7 @@
<Compile Include="Models\Authorization.cs" /> <Compile Include="Models\Authorization.cs" />
<Compile Include="Models\AuthorizationUpdate.cs" /> <Compile Include="Models\AuthorizationUpdate.cs" />
<Compile Include="Models\EmailAddress.cs" /> <Compile Include="Models\EmailAddress.cs" />
<Compile Include="Models\NewAuthorization.cs" />
<Compile Include="Models\NewRepository.cs" /> <Compile Include="Models\NewRepository.cs" />
<Compile Include="Models\Organization.cs" /> <Compile Include="Models\Organization.cs" />
<Compile Include="Models\Plan.cs" /> <Compile Include="Models\Plan.cs" />