Make credential store awaitable

The storage mechanism for credentials is very likely to be an async data
store. So might as well play it safe and make it awaitable.
This commit is contained in:
Haacked
2013-10-08 16:19:08 -07:00
parent d8ba8ae6b6
commit 90f67dd37b
9 changed files with 63 additions and 15 deletions

View File

@@ -33,6 +33,15 @@
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Reactive.Core">
<HintPath>..\packages\Rx-Core.2.1.30214.0\lib\Net45\System.Reactive.Core.dll</HintPath>
</Reference>
<Reference Include="System.Reactive.Interfaces">
<HintPath>..\packages\Rx-Interfaces.2.1.30214.0\lib\Net45\System.Reactive.Interfaces.dll</HintPath>
</Reference>
<Reference Include="System.Reactive.Linq">
<HintPath>..\packages\Rx-Linq.2.1.30214.0\lib\Net45\System.Reactive.Linq.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Xml" />

View File

@@ -1,4 +1,5 @@
using System.Net;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Octokit.Internal;
using Octokit.Tests.Helpers;
@@ -23,6 +24,25 @@ namespace Octokit.Tests.Integration
Assert.Equal("GitHub", user.Company);
}
[IntegrationTest]
public async Task ReturnsSpecifiedUserUsingAwaitableCredentialProvider()
{
var github = new GitHubClient("Octokit Test Runner", new ObservableCredentialProvider());
// Get a user by username
var user = await github.User.Get("tclem");
Assert.Equal("GitHub", user.Company);
}
class ObservableCredentialProvider : ICredentialStore
{
public async Task<Credentials> GetCredentials()
{
return await Observable.Return(AutomationSettings.Current.GitHubCredentials);
}
}
}
public class TheCurrentMethod

View File

@@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Rx-Core" version="2.1.30214.0" targetFramework="net45" />
<package id="Rx-Interfaces" version="2.1.30214.0" targetFramework="net45" />
<package id="Rx-Linq" version="2.1.30214.0" targetFramework="net45" />
<package id="xunit" version="1.9.2" targetFramework="net45" />
<package id="xunit.extensions" version="1.9.2" targetFramework="net45" />
</packages>

View File

@@ -1,4 +1,5 @@
using System;
using System.Threading.Tasks;
using NSubstitute;
using Octokit.Internal;
using Xunit;
@@ -71,7 +72,7 @@ namespace Octokit.Tests
public void IsRetrievedFromCredentialStore()
{
var credentialStore = Substitute.For<ICredentialStore>();
credentialStore.GetCredentials().Returns(new Credentials("foo", "bar"));
credentialStore.GetCredentials().Returns(Task.Factory.StartNew(() => new Credentials("foo", "bar")));
var client = new GitHubClient("Test Runner", credentialStore);
Assert.Equal("foo", client.Credentials.Login);

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Octokit.Internal
{
@@ -19,14 +20,11 @@ namespace Octokit.Internal
CredentialStore = credentialStore;
}
public void Apply(IRequest request)
public async Task Apply(IRequest request)
{
Ensure.ArgumentNotNull(request, "request");
// TODO: What if the credentials simply don't exist? We should probably
// throw an exception that can be handled and provide guidance to
// ICredentialStore implementors.
var credentials = CredentialStore.GetCredentials() ?? Credentials.Anonymous;
var credentials = await CredentialStore.GetCredentials() ?? Credentials.Anonymous;
authenticators[credentials.AuthenticationType].Authenticate(request, credentials);
}

View File

@@ -47,7 +47,9 @@ namespace Octokit
/// Convenience property for getting and setting credentials.
/// </summary>
/// <remarks>
/// Setting the credentials will change the <see cref="ICredentialStore"/> to use
/// You can use this property if you only have a single hard-coded credential. Otherwise, pass in an
/// <see cref="ICredentialStore"/> to the constructor.
/// Setting this property will change the <see cref="ICredentialStore"/> to use
/// the default <see cref="InMemoryCredentialStore"/> with just these credentials.
/// </remarks>
public Credentials Credentials

View File

@@ -171,9 +171,23 @@ namespace Octokit.Internal
get { return _authenticator.CredentialStore; }
}
/// <summary>
/// Convenience property for getting and setting credentials.
/// </summary>
/// <remarks>
/// You can use this property if you only have a single hard-coded credential. Otherwise, pass in an
/// <see cref="ICredentialStore"/> to the constructor.
/// Setting this property will change the <see cref="ICredentialStore"/> to use
/// the default <see cref="InMemoryCredentialStore"/> with just these credentials.
/// </remarks>
public Credentials Credentials
{
get { return CredentialStore.GetCredentials() ?? Credentials.Anonymous; }
get
{
var credentialTask = CredentialStore.GetCredentials();
if (credentialTask == null) return Credentials.Anonymous;
return credentialTask.Result ?? Credentials.Anonymous;
}
// Note this is for convenience. We probably shouldn't allow this to be mutable.
set
{
@@ -200,7 +214,7 @@ namespace Octokit.Internal
async Task<IResponse<T>> RunRequest<T>(IRequest request)
{
request.Headers.Add("User-Agent", UserAgent);
_authenticator.Apply(request);
await _authenticator.Apply(request);
var response = await _httpClient.Send<T>(request);
_apiInfoParser.ParseApiHttpHeaders(response);
HandleErrors(response);

View File

@@ -1,11 +1,10 @@
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
namespace Octokit.Internal
{
public interface ICredentialStore
{
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
Justification = "The credential store migth not be immediate")]
Credentials GetCredentials();
Task<Credentials> GetCredentials();
}
}

View File

@@ -1,4 +1,6 @@
namespace Octokit.Internal
using System.Threading.Tasks;
namespace Octokit.Internal
{
public class InMemoryCredentialStore : ICredentialStore
{
@@ -11,9 +13,9 @@
_credentials = credentials;
}
public Credentials GetCredentials()
public Task<Credentials> GetCredentials()
{
return _credentials;
return Task.Factory.StartNew(() => _credentials);
}
}
}