Merge remote-tracking branch 'upstream/master' into pr-495ext

Conflicts:
	Octokit/Octokit-MonoAndroid.csproj
	Octokit/Octokit-Monotouch.csproj
This commit is contained in:
Kristian Hald
2015-05-09 12:51:28 +02:00
54 changed files with 1489 additions and 199 deletions

View File

@@ -31,6 +31,56 @@ namespace Octokit.Reactive
Justification = "It's fiiiine. It's fine. Trust us.")]
IObservable<Authorization> Get(int id);
/// <summary>
/// Creates a new authorization for the specified OAuth application if an authorization for that application
/// doesnt already exist for the user; otherwise, it fails.
/// </summary>
/// <remarks>
/// This method requires authentication.
/// See the <a href="http://developer.github.com/v3/oauth/#get-or-create-an-authorization-for-a-specific-app">API documentation</a> for more information.
/// </remarks>
/// <param name="clientId">Client ID of the OAuth application for the token</param>
/// <param name="clientSecret">The client secret</param>
/// <param name="newAuthorization">Describes the new authorization to create</param>
/// <exception cref="AuthorizationException">
/// Thrown when the current user does not have permission to make this request.
/// </exception>
/// <exception cref="TwoFactorRequiredException">
/// Thrown when the current account has two-factor authentication enabled and an authentication code is required.
/// </exception>
/// <exception cref="ApiException">Thrown when a general API error occurs.</exception>
/// <returns>The created <see cref="Authorization"/>.</returns>
IObservable<ApplicationAuthorization> Create(
string clientId,
string clientSecret,
NewAuthorization newAuthorization);
/// <summary>
/// Creates a new authorization for the specified OAuth application if an authorization for that application
/// doesnt already exist for the user; otherwise, it fails.
/// </summary>
/// <remarks>
/// This method requires authentication.
/// See the <a href="http://developer.github.com/v3/oauth/#get-or-create-an-authorization-for-a-specific-app">API documentation</a> for more information.
/// </remarks>
/// <param name="clientId">Client ID of the OAuth application for the token</param>
/// <param name="clientSecret">The client secret</param>
/// <param name="twoFactorAuthenticationCode">The two-factor authentication code in response to the current user's previous challenge</param>
/// <param name="newAuthorization">Describes the new authorization to create</param>
/// <exception cref="AuthorizationException">
/// Thrown when the current user does not have permission to make this request.
/// </exception>
/// <exception cref="TwoFactorRequiredException">
/// Thrown when the current account has two-factor authentication enabled and an authentication code is required.
/// </exception>
/// <exception cref="ApiException">Thrown when a general API error occurs.</exception>
/// <returns>The created <see cref="Authorization"/>.</returns>
IObservable<ApplicationAuthorization> Create(
string clientId,
string clientSecret,
NewAuthorization newAuthorization,
string twoFactorAuthenticationCode);
/// <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
@@ -133,10 +183,34 @@ namespace Octokit.Reactive
IObservable<Authorization> Update(int id, AuthorizationUpdate authorizationUpdate);
/// <summary>
/// Deletes an <see cref="Authorization"/>.
/// Deletes the specified <see cref="Authorization"/>.
/// </summary>
/// <param name="id">The systemwide id of the authorization</param>
/// <returns></returns>
/// <remarks>
/// This method requires authentication.
/// See the <a href="http://developer.github.com/v3/oauth/#delete-an-authorization">API
/// documentation</a> for more details.
/// </remarks>
/// <param name="id">The system-wide ID of the authorization to delete</param>
/// <exception cref="AuthorizationException">
/// Thrown when the current user does not have permission to make the request.
/// </exception>
/// <exception cref="ApiException">Thrown when a general API error occurs.</exception>
IObservable<Unit> Delete(int id);
/// <summary>
/// Deletes the specified <see cref="Authorization"/>.
/// </summary>
/// <remarks>
/// This method requires authentication.
/// See the <a href="http://developer.github.com/v3/oauth/#delete-an-authorization">API
/// documentation</a> for more details.
/// </remarks>
/// <param name="id">The system-wide ID of the authorization to delete</param>
/// <param name="twoFactorAuthenticationCode">Two factor authorization code</param>
/// <exception cref="AuthorizationException">
/// Thrown when the current user does not have permission to make the request.
/// </exception>
/// <exception cref="ApiException">Thrown when a general API error occurs.</exception>
IObservable<Unit> Delete(int id, string twoFactorAuthenticationCode);
}
}

View File

@@ -91,6 +91,24 @@ namespace Octokit.Reactive
/// <param name="since">Only gists updated at or after this time are returned</param>
IObservable<Gist> GetAllForUser(string user, DateTimeOffset since);
/// <summary>
/// List gist commits
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/gists/#list-gists-commits
/// </remarks>
/// <param name="id">The id of the gist</param>
IObservable<GistHistory> GetAllCommits(string id);
/// <summary>
/// List gist forks
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/gists/#list-gists-forks
/// </remarks>
/// <param name="id">The id of the gist</param>
IObservable<GistFork> GetAllForks(string id);
/// <summary>
/// Creates a new gist
/// </summary>

View File

@@ -24,6 +24,45 @@ namespace Octokit.Reactive
/// <returns></returns>
IObservable<string> GetReadmeHtml(string owner, string name);
/// <summary>
/// This method will return a 302 to a URL to download a tarball or zipball archive for a repository.
/// Please make sure your HTTP framework is configured to follow redirects or you will need to use the
/// Location header to make a second GET request.
/// Note: For private repositories, these links are temporary and expire quickly.
/// </summary>
/// <remarks>https://developer.github.com/v3/repos/contents/#get-archive-link</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <returns></returns>
IObservable<string> GetArchiveLink(string owner, string name);
/// <summary>
/// This method will return a 302 to a URL to download a tarball or zipball archive for a repository.
/// Please make sure your HTTP framework is configured to follow redirects or you will need to use the
/// Location header to make a second GET request.
/// Note: For private repositories, these links are temporary and expire quickly.
/// </summary>
/// <remarks>https://developer.github.com/v3/repos/contents/#get-archive-link</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="archiveFormat">The format of the archive. Can be either tarball or zipball</param>
/// <returns></returns>
IObservable<string> GetArchiveLink(string owner, string name, ArchiveFormat archiveFormat);
/// <summary>
/// This method will return a 302 to a URL to download a tarball or zipball archive for a repository.
/// Please make sure your HTTP framework is configured to follow redirects or you will need to use the
/// Location header to make a second GET request.
/// Note: For private repositories, these links are temporary and expire quickly.
/// </summary>
/// <remarks>https://developer.github.com/v3/repos/contents/#get-archive-link</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="archiveFormat">The format of the archive. Can be either tarball or zipball</param>
/// <param name="reference">A valid Git reference.</param>
/// <returns></returns>
IObservable<string> GetArchiveLink(string owner, string name, ArchiveFormat archiveFormat, string reference);
/// <summary>
/// Returns the contents of a file or directory in a repository.
/// </summary>

View File

@@ -45,6 +45,71 @@ namespace Octokit.Reactive
return _client.Get(id).ToObservable();
}
/// <summary>
/// Creates a new authorization for the specified OAuth application if an authorization for that application
/// doesnt already exist for the user; otherwise, it fails.
/// </summary>
/// <remarks>
/// This method requires authentication.
/// See the <a href="http://developer.github.com/v3/oauth/#get-or-create-an-authorization-for-a-specific-app">API documentation</a> for more information.
/// </remarks>
/// <param name="clientId">Client ID of the OAuth application for the token</param>
/// <param name="clientSecret">The client secret</param>
/// <param name="newAuthorization">Describes the new authorization to create</param>
/// <exception cref="AuthorizationException">
/// Thrown when the current user does not have permission to make this request.
/// </exception>
/// <exception cref="TwoFactorRequiredException">
/// Thrown when the current account has two-factor authentication enabled and an authentication code is required.
/// </exception>
/// <exception cref="ApiException">Thrown when a general API error occurs.</exception>
/// <returns>The created <see cref="Authorization"/>.</returns>
public IObservable<ApplicationAuthorization> Create(
string clientId,
string clientSecret,
NewAuthorization newAuthorization)
{
Ensure.ArgumentNotNullOrEmptyString(clientId, "clientId");
Ensure.ArgumentNotNullOrEmptyString(clientSecret, "clientSecret");
Ensure.ArgumentNotNull(newAuthorization, "authorization");
return _client.Create(clientId, clientSecret, newAuthorization).ToObservable();
}
/// <summary>
/// Creates a new authorization for the specified OAuth application if an authorization for that application
/// doesnt already exist for the user; otherwise, it fails.
/// </summary>
/// <remarks>
/// This method requires authentication.
/// See the <a href="http://developer.github.com/v3/oauth/#get-or-create-an-authorization-for-a-specific-app">API documentation</a> for more information.
/// </remarks>
/// <param name="clientId">Client ID of the OAuth application for the token</param>
/// <param name="clientSecret">The client secret</param>
/// <param name="twoFactorAuthenticationCode">The two-factor authentication code in response to the current user's previous challenge</param>
/// <param name="newAuthorization">Describes the new authorization to create</param>
/// <exception cref="AuthorizationException">
/// Thrown when the current user does not have permission to make this request.
/// </exception>
/// <exception cref="TwoFactorRequiredException">
/// Thrown when the current account has two-factor authentication enabled and an authentication code is required.
/// </exception>
/// <exception cref="ApiException">Thrown when a general API error occurs.</exception>
/// <returns>The created <see cref="Authorization"/>.</returns>
public IObservable<ApplicationAuthorization> Create(
string clientId,
string clientSecret,
NewAuthorization newAuthorization,
string twoFactorAuthenticationCode)
{
Ensure.ArgumentNotNullOrEmptyString(clientId, "clientId");
Ensure.ArgumentNotNullOrEmptyString(clientSecret, "clientSecret");
Ensure.ArgumentNotNull(newAuthorization, "authorization");
Ensure.ArgumentNotNullOrEmptyString(twoFactorAuthenticationCode, "twoFactorAuthenticationCode");
return _client.Create(clientId, clientSecret, newAuthorization, twoFactorAuthenticationCode).ToObservable();
}
/// <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
@@ -201,13 +266,40 @@ namespace Octokit.Reactive
}
/// <summary>
/// Deletes an <see cref="Authorization"/>.
/// Deletes the specified <see cref="Authorization"/>.
/// </summary>
/// <param name="id">The systemwide id of the authorization</param>
/// <returns></returns>
/// <remarks>
/// This method requires authentication.
/// See the <a href="http://developer.github.com/v3/oauth/#delete-an-authorization">API
/// documentation</a> for more details.
/// </remarks>
/// <param name="id">The system-wide ID of the authorization to delete</param>
/// <exception cref="AuthorizationException">
/// Thrown when the current user does not have permission to make the request.
/// </exception>
/// <exception cref="ApiException">Thrown when a general API error occurs.</exception>
public IObservable<Unit> Delete(int id)
{
return _client.Delete(id).ToObservable();
}
/// <summary>
/// Deletes the specified <see cref="Authorization"/>.
/// </summary>
/// <remarks>
/// This method requires authentication.
/// See the <a href="http://developer.github.com/v3/oauth/#delete-an-authorization">API
/// documentation</a> for more details.
/// </remarks>
/// <param name="id">The system-wide ID of the authorization to delete</param>
/// <param name="twoFactorAuthenticationCode">Two factor authorization code</param>
/// <exception cref="AuthorizationException">
/// Thrown when the current user does not have permission to make the request.
/// </exception>
/// <exception cref="ApiException">Thrown when a general API error occurs.</exception>
public IObservable<Unit> Delete(int id, string twoFactorAuthenticationCode)
{
return _client.Delete(id, twoFactorAuthenticationCode).ToObservable();
}
}
}

View File

@@ -180,6 +180,34 @@ namespace Octokit.Reactive
return _connection.GetAndFlattenAllPages<Gist>(ApiUrls.UsersGists(user), request.ToParametersDictionary());
}
/// <summary>
/// List gist commits
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/gists/#list-gists-commits
/// </remarks>
/// <param name="id">The id of the gist</param>
public IObservable<GistHistory> GetAllCommits(string id)
{
Ensure.ArgumentNotNullOrEmptyString(id, "id");
return _connection.GetAndFlattenAllPages<GistHistory>(ApiUrls.GistCommits(id));
}
/// <summary>
/// List gist forks
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/gists/#list-gists-forks
/// </remarks>
/// <param name="id">The id of the gist</param>
public IObservable<GistFork> GetAllForks(string id)
{
Ensure.ArgumentNotNullOrEmptyString(id, "id");
return _connection.GetAndFlattenAllPages<GistFork>(ApiUrls.ForkGist(id));
}
/// <summary>
/// Edits a gist
/// </summary>

View File

@@ -116,7 +116,9 @@ namespace Octokit.Reactive
{
Ensure.ArgumentNotNull(request, "request");
return _connection.GetAndFlattenAllPages<Repository>(ApiUrls.AllPublicRepositories(), request.ToParametersDictionary());
var url = ApiUrls.AllPublicRepositories(request.Since);
return _connection.GetAndFlattenAllPages<Repository>(url);
}
/// <summary>

View File

@@ -49,6 +49,57 @@ namespace Octokit.Reactive
}
/// <summary>
/// This method will return a 302 to a URL to download a tarball or zipball archive for a repository.
/// Please make sure your HTTP framework is configured to follow redirects or you will need to use the
/// Location header to make a second GET request.
/// Note: For private repositories, these links are temporary and expire quickly.
/// </summary>
/// <remarks>https://developer.github.com/v3/repos/contents/#get-archive-link</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <returns></returns>
public IObservable<string> GetArchiveLink(string owner, string name)
{
return GetArchiveLink(owner, name, ArchiveFormat.Tarball, string.Empty);
}
/// <summary>
/// This method will return a 302 to a URL to download a tarball or zipball archive for a repository.
/// Please make sure your HTTP framework is configured to follow redirects or you will need to use the
/// Location header to make a second GET request.
/// Note: For private repositories, these links are temporary and expire quickly.
/// </summary>
/// <remarks>https://developer.github.com/v3/repos/contents/#get-archive-link</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="archiveFormat">The format of the archive. Can be either tarball or zipball</param>
/// <returns></returns>
public IObservable<string> GetArchiveLink(string owner, string name, ArchiveFormat archiveFormat)
{
return GetArchiveLink(owner, name, archiveFormat, String.Empty);
}
/// <summary>
/// This method will return a 302 to a URL to download a tarball or zipball archive for a repository.
/// Please make sure your HTTP framework is configured to follow redirects or you will need to use the
/// Location header to make a second GET request.
/// Note: For private repositories, these links are temporary and expire quickly.
/// </summary>
/// <remarks>https://developer.github.com/v3/repos/contents/#get-archive-link</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="archiveFormat">The format of the archive. Can be either tarball or zipball</param>
/// <param name="reference">A valid Git reference.</param>
/// <returns></returns>
public IObservable<string> GetArchiveLink(string owner, string name, ArchiveFormat archiveFormat, string reference)
{
Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
Ensure.ArgumentNotNullOrEmptyString(name, "name");
return _client.Repository.Content.GetArchiveLink(owner, name, archiveFormat, reference).ToObservable();
}
/// <summary>
/// Returns the contents of a file or directory in a repository.
/// </summary>

View File

@@ -33,8 +33,7 @@ namespace Octokit
string clientId,
string clientSecret,
NewAuthorization newAuthorization,
Func<TwoFactorRequiredException, IObservable<TwoFactorChallengeResult>> twoFactorChallengeHandler
)
Func<TwoFactorRequiredException, IObservable<TwoFactorChallengeResult>> twoFactorChallengeHandler)
{
Ensure.ArgumentNotNull(authorizationsClient, "authorizationsClient");
Ensure.ArgumentNotNullOrEmptyString(clientId, "clientId");
@@ -55,5 +54,147 @@ namespace Octokit
newAuthorization,
result.AuthenticationCode)));
}
/// <summary>
/// This method will create a new authorization for the specified OAuth application. If an authorization
/// for that application already exists for the user and fingerprint, it'll delete the existing one and
/// recreate it.
/// </summary>
/// <remarks>
/// <para>
/// This method is typically used to initiate an application authentication flow.
/// 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>
/// <param name="retryInvalidTwoFactorCode">If true, instead of completing when the two factor code supplied
/// is invalid, we go through the whole cycle again and prompt the two factor dialog.</param>
/// <returns></returns>
public static IObservable<ApplicationAuthorization> CreateAndDeleteExistingApplicationAuthorization(
this IObservableAuthorizationsClient authorizationsClient,
string clientId,
string clientSecret,
NewAuthorization newAuthorization,
Func<TwoFactorAuthorizationException, IObservable<TwoFactorChallengeResult>> twoFactorChallengeHandler,
bool retryInvalidTwoFactorCode)
{
return authorizationsClient.CreateAndDeleteExistingApplicationAuthorization(
clientId,
clientSecret,
newAuthorization,
twoFactorChallengeHandler,
null,
retryInvalidTwoFactorCode);
}
public static IObservable<ApplicationAuthorization> CreateAndDeleteExistingApplicationAuthorization(
this IObservableAuthorizationsClient authorizationsClient,
string clientId,
string clientSecret,
NewAuthorization newAuthorization,
Func<TwoFactorAuthorizationException, IObservable<TwoFactorChallengeResult>> twoFactorChallengeHandler,
string twoFactorAuthenticationCode,
bool retryInvalidTwoFactorCode)
{
Ensure.ArgumentNotNull(authorizationsClient, "authorizationsClient");
Ensure.ArgumentNotNullOrEmptyString(clientId, "clientId");
Ensure.ArgumentNotNullOrEmptyString(clientSecret, "clientSecret");
Ensure.ArgumentNotNull(newAuthorization, "newAuthorization");
// If retryInvalidTwoFactorCode is false, then we only show the TwoFactorDialog when we catch
// a TwoFactorRequiredException. If it's true, we show it for TwoFactorRequiredException and
// TwoFactorChallengeFailedException
Func<TwoFactorAuthorizationException, IObservable<TwoFactorChallengeResult>> twoFactorHandler = ex =>
retryInvalidTwoFactorCode || ex is TwoFactorRequiredException
? twoFactorChallengeHandler(ex)
: Observable.Throw<TwoFactorChallengeResult>(ex);
return authorizationsClient.CreateAuthorizationAndDeleteExisting(
clientId,
clientSecret,
newAuthorization,
twoFactorAuthenticationCode)
.Catch<ApplicationAuthorization, TwoFactorAuthorizationException>(
exception => twoFactorHandler(exception)
.SelectMany(result =>
result.ResendCodeRequested
? authorizationsClient.CreateAndDeleteExistingApplicationAuthorization(
clientId,
clientSecret,
newAuthorization,
twoFactorHandler,
null, // twoFactorAuthenticationCode
retryInvalidTwoFactorCode)
: authorizationsClient.CreateAndDeleteExistingApplicationAuthorization(
clientId,
clientSecret,
newAuthorization,
twoFactorHandler,
result.AuthenticationCode,
retryInvalidTwoFactorCode)));
}
// If the Application Authorization already exists, the result might have an empty string as the token. This is
// because GitHub.com no longer stores the token, but stores a hashed version of it. It is assumed that clients
// will store the token locally.
// The only reason to be calling GetOrCreateApplicationAuthentication is pretty much the case when you are
// logging in and thus don't have the token already. So if the token returned is an empty string, we'll go
// ahead and delete it for you and then recreate it.
static IObservable<ApplicationAuthorization> CreateAuthorizationAndDeleteExisting(
this IObservableAuthorizationsClient authorizationsClient,
string clientId,
string clientSecret,
NewAuthorization newAuthorization,
string twoFactorAuthenticationCode = null)
{
return authorizationsClient.GetOrCreateAuthorizationUnified(
clientId,
clientSecret,
newAuthorization,
twoFactorAuthenticationCode)
.SelectMany(authorization => string.IsNullOrEmpty(authorization.Token)
? authorizationsClient.Delete(authorization.Id, twoFactorAuthenticationCode)
.SelectMany(_ =>
authorizationsClient.CreateNewAuthorization(
clientId,
clientSecret,
newAuthorization,
twoFactorAuthenticationCode))
: Observable.Return(authorization));
}
static IObservable<ApplicationAuthorization> GetOrCreateAuthorizationUnified(
this IObservableAuthorizationsClient authorizationsClient,
string clientId,
string clientSecret,
NewAuthorization newAuthorization,
string twoFactorAuthenticationCode = null)
{
return string.IsNullOrEmpty(twoFactorAuthenticationCode)
? authorizationsClient.GetOrCreateApplicationAuthentication(clientId, clientSecret, newAuthorization)
: authorizationsClient.GetOrCreateApplicationAuthentication(clientId, clientSecret, newAuthorization, twoFactorAuthenticationCode);
}
static IObservable<ApplicationAuthorization> CreateNewAuthorization(
this IObservableAuthorizationsClient authorizationsClient,
string clientId,
string clientSecret,
NewAuthorization newAuthorization,
string twoFactorAuthenticationCode = null)
{
return string.IsNullOrEmpty(twoFactorAuthenticationCode)
? authorizationsClient.Create(clientId, clientSecret, newAuthorization)
: authorizationsClient.Create(clientId, clientSecret, newAuthorization, twoFactorAuthenticationCode);
}
}
}

View File

@@ -22,10 +22,9 @@ namespace Octokit.Tests.Integration.Clients
Helper.ClientSecret,
newAuthorization);
Assert.NotNull(created);
Assert.False(String.IsNullOrWhiteSpace(created.Token));
Assert.True(String.IsNullOrWhiteSpace(created.TokenLastEight));
Assert.True(String.IsNullOrWhiteSpace(created.HashedToken));
Assert.False(String.IsNullOrWhiteSpace(created.TokenLastEight));
Assert.False(String.IsNullOrWhiteSpace(created.HashedToken));
// we can then query it through the regular API
var get = await client.Authorization.Get(created.Id);
@@ -42,13 +41,11 @@ namespace Octokit.Tests.Integration.Clients
Assert.Equal(created.Id, getExisting.Id);
// NOTE: the old API will continue to return the full
// token if no Fingerprint is included
Assert.False(String.IsNullOrWhiteSpace(getExisting.Token));
// NOTE: and these new values are not included
Assert.True(String.IsNullOrWhiteSpace(getExisting.TokenLastEight));
Assert.True(String.IsNullOrWhiteSpace(getExisting.HashedToken));
// the token is no longer returned for subsequent calls
Assert.True(String.IsNullOrWhiteSpace(getExisting.Token));
// however the hashed and last 8 characters are available
Assert.False(String.IsNullOrWhiteSpace(getExisting.TokenLastEight));
Assert.False(String.IsNullOrWhiteSpace(getExisting.HashedToken));
await client.Authorization.Delete(created.Id);
}

View File

@@ -34,7 +34,7 @@ namespace Octokit.Tests.Integration.Clients
Assert.True(_events.All(e => e.Payload != null));
}
[IntegrationTest]
[IntegrationTest(Skip = "no longer able to access this event")]
public void IssueCommentPayloadEventDeserializesCorrectly()
{
var commentEvent = _events.FirstOrDefault(e => e.Id == "2628548686");
@@ -49,7 +49,7 @@ namespace Octokit.Tests.Integration.Clients
Assert.Equal(742, commentPayload.Issue.Number);
}
[IntegrationTest]
[IntegrationTest(Skip = "no longer able to access this event")]
public void PushEventPayloadDeserializesCorrectly()
{
var pushEvent = _events.FirstOrDefault(e => e.Id == "2628858765");
@@ -65,7 +65,7 @@ namespace Octokit.Tests.Integration.Clients
Assert.Equal(1, pushPayload.Size);
}
[IntegrationTest]
[IntegrationTest(Skip = "no longer able to access this event")]
public void PREventPayloadDeserializesCorrectly()
{
var prEvent = _events.FirstOrDefault(e => e.Id == "2628718313");
@@ -79,7 +79,7 @@ namespace Octokit.Tests.Integration.Clients
Assert.Equal(743, prPayload.PullRequest.Number);
}
[IntegrationTest]
[IntegrationTest(Skip = "no longer able to access this event")]
public void PRReviewCommentEventPayloadDeserializesCorrectly()
{
var prrcEvent = _events.First(e => e.Id == "2623246246");

View File

@@ -117,4 +117,16 @@ public class GistsClientTests
await _fixture.Delete(createdGist.Id);
}
[IntegrationTest]
public async Task CanGetGistChildren()
{
// Test History/Commits
var commits = await _fixture.GetAllCommits(testGistId);
Assert.NotEmpty(commits);
// Test Forks
var forks = await _fixture.GetAllForks(testGistId);
Assert.NotEmpty(forks);
}
}

View File

@@ -553,7 +553,7 @@ public class RepositoriesClientTests
Assert.True(repositories.Count > 80);
}
[IntegrationTest]
[IntegrationTest(Skip = "Takes too long to run.")]
public async Task ReturnsAllPublicRepositoriesSinceLastSeen()
{
var github = Helper.GetAuthenticatedClient();

View File

@@ -12,10 +12,7 @@ namespace Octokit.Tests.Integration.Clients
[IntegrationTest]
public async Task ReturnsReadmeForSeeGit()
{
var github = new GitHubClient(new ProductHeaderValue("OctokitTests"))
{
Credentials = Helper.Credentials
};
var github = Helper.GetAuthenticatedClient();
var readme = await github.Repository.Content.GetReadme("octokit", "octokit.net");
Assert.Equal("README.md", readme.Name);
@@ -28,10 +25,7 @@ namespace Octokit.Tests.Integration.Clients
[IntegrationTest]
public async Task ReturnsReadmeHtmlForSeeGit()
{
var github = new GitHubClient(new ProductHeaderValue("OctokitTests"))
{
Credentials = Helper.Credentials
};
var github = Helper.GetAuthenticatedClient();
var readmeHtml = await github.Repository.Content.GetReadmeHtml("octokit", "octokit.net");
Assert.True(readmeHtml.StartsWith("<div class="));
@@ -45,10 +39,7 @@ namespace Octokit.Tests.Integration.Clients
[IntegrationTest]
public async Task GetsFileContent()
{
var github = new GitHubClient(new ProductHeaderValue("OctokitTests"))
{
Credentials = Helper.Credentials
};
var github = Helper.GetAuthenticatedClient();
var contents = await github
.Repository
@@ -63,10 +54,7 @@ namespace Octokit.Tests.Integration.Clients
[IntegrationTest]
public async Task GetsDirectoryContent()
{
var github = new GitHubClient(new ProductHeaderValue("OctokitTests"))
{
Credentials = Helper.Credentials
};
var github = Helper.GetAuthenticatedClient();
var contents = await github
.Repository
@@ -81,10 +69,8 @@ namespace Octokit.Tests.Integration.Clients
[IntegrationTest]
public async Task CrudTest()
{
var client = new GitHubClient(new ProductHeaderValue("OctokitTests"))
{
Credentials = Helper.Credentials
};
var client = Helper.GetAuthenticatedClient();
Repository repository = null;
try
{
@@ -128,5 +114,44 @@ namespace Octokit.Tests.Integration.Clients
Helper.DeleteRepo(repository);
}
}
[IntegrationTest]
public async Task GetsArchiveLinkAsTarball()
{
var github = Helper.GetAuthenticatedClient();
var archiveLink = await github
.Repository
.Content
.GetArchiveLink("octokit", "octokit.net");
Assert.Equal("https://codeload.github.com/octokit/octokit.net/legacy.tar.gz/master", archiveLink);
}
[IntegrationTest]
public async Task GetsArchiveLinkAsZipball()
{
var github = Helper.GetAuthenticatedClient();
var archiveLink = await github
.Repository
.Content
.GetArchiveLink("octokit", "octokit.net", ArchiveFormat.Zipball, "");
Assert.Equal("https://codeload.github.com/octokit/octokit.net/legacy.zip/master", archiveLink);
}
[IntegrationTest]
public async Task GetsArchiveLinkForReleaseBranchAsTarball()
{
var github = Helper.GetAuthenticatedClient();
var archiveLink = await github
.Repository
.Content
.GetArchiveLink("alfhenrik", "ScriptCs.OctoKit", ArchiveFormat.Tarball, "dev");
Assert.Equal("https://codeload.github.com/alfhenrik/ScriptCs.OctoKit/legacy.tar.gz/dev", archiveLink);
}
}
}

View File

@@ -32,5 +32,25 @@ public class HttpClientAdapterTests
Assert.Equal(78, imageBytes[2]);
Assert.Equal(130, imageBytes.Last());
}
[IntegrationTest]
public async Task CanCancelARequest()
{
var httpClient = new HttpClientAdapter();
var request = new Request
{
BaseAddress = new Uri("https://github.global.ssl.fastly.net/", UriKind.Absolute),
Endpoint = new Uri("/images/icons/emoji/poop.png?v=5", UriKind.RelativeOrAbsolute),
AllowAutoRedirect = true,
Method = HttpMethod.Get,
Timeout = TimeSpan.FromMilliseconds(10)
};
var response = httpClient.Send(request, CancellationToken.None);
await Task.Delay(TimeSpan.FromSeconds(2));
Assert.True(response.IsCanceled);
}
}
}

View File

@@ -36,8 +36,7 @@ namespace Octokit.Tests.Clients
client.Received().GetAll<Authorization>(
Arg.Is<Uri>(u => u.ToString() == "authorizations"),
null,
Arg.Any<string>());
null);
}
}
@@ -53,8 +52,7 @@ namespace Octokit.Tests.Clients
client.Received().Get<Authorization>(
Arg.Is<Uri>(u => u.ToString() == "authorizations/1"),
null,
Arg.Any<string>());
null);
}
}
@@ -238,9 +236,7 @@ namespace Octokit.Tests.Clients
authEndpoint.GetOrCreateApplicationAuthentication("clientId", "secret", data);
client.Received().Put<ApplicationAuthorization>(Arg.Is<Uri>(u => u.ToString() == "authorizations/clients/clientId/ha-ha-fingerprint"),
Args.Object,
Args.String,
Args.String); // NOTE: preview API
Args.Object);
}
}
@@ -256,8 +252,7 @@ namespace Octokit.Tests.Clients
client.Received().Get<ApplicationAuthorization>(
Arg.Is<Uri>(u => u.ToString() == "applications/clientId/tokens/accessToken"),
null,
Arg.Any<string>());
null);
}
[Fact]

View File

@@ -122,6 +122,44 @@ public class GistsClientTests
}
}
public class TheGetChildrenMethods
{
[Fact]
public async Task EnsureNonNullArguments()
{
var connection = Substitute.For<IApiConnection>();
var client = new GistsClient(connection);
await Assert.ThrowsAsync<ArgumentNullException>(() => client.GetAllCommits(null));
await Assert.ThrowsAsync<ArgumentException>(() => client.GetAllCommits(""));
await Assert.ThrowsAsync<ArgumentNullException>(() => client.GetAllForks(null));
await Assert.ThrowsAsync<ArgumentException>(() => client.GetAllForks(""));
}
[Fact]
public void RequestsCorrectGetCommitsUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new GistsClient(connection);
client.GetAllCommits("9257657");
connection.Received().GetAll<GistHistory>(Arg.Is<Uri>(u => u.ToString() == "gists/9257657/commits"));
}
[Fact]
public void RequestsCorrectGetForksUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new GistsClient(connection);
client.GetAllForks("9257657");
connection.Received().GetAll<GistFork>(Arg.Is<Uri>(u => u.ToString() == "gists/9257657/forks"));
}
}
public class TheCreateMethod
{
[Fact]

View File

@@ -285,8 +285,7 @@ namespace Octokit.Tests.Clients
client.GetAllPublic(new PublicRepositoryRequest(364));
connection.Received()
.GetAll<Repository>(Arg.Is<Uri>(u => u.ToString() == "/repositories"),
Arg.Any<Dictionary<string, string>>());
.GetAll<Repository>(Arg.Is<Uri>(u => u.ToString() == "/repositories?since=364"));
}
[Fact]
@@ -298,9 +297,7 @@ namespace Octokit.Tests.Clients
client.GetAllPublic(new PublicRepositoryRequest(364));
connection.Received()
.GetAll<Repository>(Arg.Is<Uri>(u => u.ToString() == "/repositories"),
Arg.Is<Dictionary<string, string>>(d => d.Count == 1
&& d["since"] == "364"));
.GetAll<Repository>(Arg.Is<Uri>(u => u.ToString() == "/repositories?since=364"));
}
}

View File

@@ -2,6 +2,7 @@
using System.Text;
using System.Threading.Tasks;
using NSubstitute;
using Octokit.Tests.Helpers;
using Xunit;
namespace Octokit.Tests.Clients
@@ -53,5 +54,59 @@ namespace Octokit.Tests.Clients
Assert.Equal("<html>README</html>", readme);
}
}
public class TheGetArchiveLinkMethod
{
[Fact]
public async Task ReturnsArchiveLinkWithDefaults()
{
var connection = Substitute.For<IApiConnection>();
connection.GetRedirect(Args.Uri).Returns(Task.FromResult("https://codeload.github.com/fake/repo/legacy.tar.gz/master"));
var contentsClient = new RepositoryContentsClient(connection);
var archiveLink = await contentsClient.GetArchiveLink("fake", "repo");
connection.Received().GetRedirect(Arg.Is<Uri>(u => u.ToString() == "repos/fake/repo/tarball/"));
Assert.Equal("https://codeload.github.com/fake/repo/legacy.tar.gz/master", archiveLink);
}
[Fact]
public async Task ReturnsArchiveLinkAsZipball()
{
var connection = Substitute.For<IApiConnection>();
connection.GetRedirect(Args.Uri).Returns(Task.FromResult("https://codeload.github.com/fake/repo/legacy.tar.gz/master"));
var contentsClient = new RepositoryContentsClient(connection);
var archiveLink = await contentsClient.GetArchiveLink("fake", "repo", ArchiveFormat.Zipball);
connection.Received().GetRedirect(Arg.Is<Uri>(u => u.ToString() == "repos/fake/repo/zipball/"));
Assert.Equal("https://codeload.github.com/fake/repo/legacy.tar.gz/master", archiveLink);
}
[Fact]
public async Task ReturnsArchiveLinkWithSpecifiedValues()
{
var connection = Substitute.For<IApiConnection>();
connection.GetRedirect(Args.Uri).Returns(Task.FromResult("https://codeload.github.com/fake/repo/legazy.zip/release"));
var contentsClient = new RepositoryContentsClient(connection);
var archiveLink = await contentsClient.GetArchiveLink("fake", "repo", ArchiveFormat.Zipball, "release");
connection.Received().GetRedirect(Arg.Is<Uri>(u => u.ToString() == "repos/fake/repo/zipball/release"));
Assert.Equal("https://codeload.github.com/fake/repo/legazy.zip/release", archiveLink);
}
[Fact]
public async Task EnsuresArgumentsNotNull()
{
var connection = Substitute.For<IApiConnection>();
var contentsClient = new RepositoryContentsClient(connection);
AssertEx.Throws<ArgumentNullException>(async () => await contentsClient.GetArchiveLink(null, "name"));
AssertEx.Throws<ArgumentNullException>(async () => await contentsClient.GetArchiveLink("owner", null));
AssertEx.Throws<ArgumentException>(async () => await contentsClient.GetArchiveLink("", "name"));
AssertEx.Throws<ArgumentException>(async () => await contentsClient.GetArchiveLink("owner", ""));
}
}
}
}

View File

@@ -16,11 +16,15 @@ namespace Octokit.Tests.Http
{
public class TheBuildRequestMessageMethod
{
readonly Uri _endpoint = new Uri("/ha-ha-business", UriKind.Relative);
[Fact]
public void AddsHeadersToRequestMessage()
{
var request = new Request
{
BaseAddress = GitHubClient.GitHubApiUrl,
Endpoint = _endpoint,
Method = HttpMethod.Post,
Headers =
{
@@ -47,6 +51,8 @@ namespace Octokit.Tests.Http
{
var request = new Request
{
BaseAddress = GitHubClient.GitHubApiUrl,
Endpoint = _endpoint,
Method = HttpMethod.Post,
Body = "{}",
ContentType = "text/plain"
@@ -64,6 +70,8 @@ namespace Octokit.Tests.Http
{
var request = new Request
{
BaseAddress = GitHubClient.GitHubApiUrl,
Endpoint = _endpoint,
Method = HttpMethod.Post,
Body = new MemoryStream(),
ContentType = "text/plain"
@@ -82,6 +90,8 @@ namespace Octokit.Tests.Http
{
var request = new Request
{
BaseAddress = GitHubClient.GitHubApiUrl,
Endpoint = _endpoint,
Method = HttpMethod.Post,
Body = new FormUrlEncodedContent(new Dictionary<string, string> {{"foo", "bar"}})
};

View File

@@ -75,6 +75,7 @@
<Compile Include="Clients\IssuesClientTests.cs" />
<Compile Include="Clients\IssuesEventsClientTests.cs" />
<Compile Include="Clients\IssuesLabelsClientTests.cs" />
<Compile Include="Clients\MergingClientTests.cs" />
<Compile Include="Clients\MilestonesClientTests.cs" />
<Compile Include="Clients\MiscellaneousClientTests.cs" />
<Compile Include="Clients\NotificationsClientTests.cs" />
@@ -149,6 +150,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Helpers\StringExtensionsTests.cs" />
<Compile Include="Clients\RepositoriesClientTests.cs" />
<Compile Include="SelfTests.cs" />
<Compile Include="SimpleJsonSerializerTests.cs" />
<Compile Include="Clients\UsersClientTests.cs" />
<Compile Include="Clients\TeamsClientTests.cs" />

View File

@@ -75,6 +75,7 @@
<Compile Include="Clients\IssuesClientTests.cs" />
<Compile Include="Clients\IssuesEventsClientTests.cs" />
<Compile Include="Clients\IssuesLabelsClientTests.cs" />
<Compile Include="Clients\MergingClientTests.cs" />
<Compile Include="Clients\MilestonesClientTests.cs" />
<Compile Include="Clients\MiscellaneousClientTests.cs" />
<Compile Include="Clients\NotificationsClientTests.cs" />
@@ -147,6 +148,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Helpers\StringExtensionsTests.cs" />
<Compile Include="Clients\RepositoriesClientTests.cs" />
<Compile Include="SelfTests.cs" />
<Compile Include="SimpleJsonSerializerTests.cs" />
<Compile Include="Clients\UsersClientTests.cs" />
<Compile Include="Clients\TeamsClientTests.cs" />

View File

@@ -181,6 +181,7 @@
<Compile Include="Reactive\ObservablePullRequestReviewCommentsClientTests.cs" />
<Compile Include="Reactive\ObservableRepositoriesClientTests.cs" />
<Compile Include="Reactive\ObservableRepositoryDeployKeysClientTests.cs" />
<Compile Include="Reactive\ObservableGistsTests.cs" />
<Compile Include="Reactive\ObservableStarredClientTests.cs" />
<Compile Include="Reactive\ObservableStatisticsClientTests.cs" />
<Compile Include="Reactive\ObservableTreesClientTests.cs" />

View File

@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using System.Threading.Tasks;
using NSubstitute;
using Octokit;
using Octokit.Internal;
using Octokit.Reactive;
using Octokit.Reactive.Internal;
using Octokit.Tests.Helpers;
using Xunit;
using Xunit.Extensions;
namespace Octokit.Tests.Reactive
{
public class ObservableGistsTests
{
public class TheGetChildrenMethods
{
[Fact]
public async Task EnsureNonNullArguments()
{
var client = new ObservableGistsClient(Substitute.For<IGitHubClient>());
await AssertEx.Throws<ArgumentNullException>(async () => await client.GetAllCommits(null));
await AssertEx.Throws<ArgumentException>(async () => await client.GetAllCommits(""));
await AssertEx.Throws<ArgumentNullException>(async () => await client.GetAllForks(null));
await AssertEx.Throws<ArgumentException>(async () => await client.GetAllForks(""));
}
[Fact]
public void RequestsCorrectGetCommitsUrl()
{
var github = Substitute.For<IGitHubClient>();
var client = new ObservableGistsClient(github);
var expected = new Uri("gists/9257657/commits", UriKind.Relative);
client.GetAllCommits("9257657");
github.Connection.Received(1).Get<List<GistHistory>>(expected, Arg.Any<IDictionary<string, string>>(), null);
}
[Fact]
public void RequestsCorrectGetForksUrl()
{
var github = Substitute.For<IGitHubClient>();
var client = new ObservableGistsClient(github);
var expected = new Uri("gists/9257657/forks", UriKind.Relative);
client.GetAllForks("9257657");
github.Connection.Received(1).Get<List<GistFork>>(expected, Arg.Any<IDictionary<string, string>>(), null);
}
}
}
}

View File

@@ -163,7 +163,7 @@ namespace Octokit.Tests.Reactive
[Fact]
public async Task ReturnsEveryPageOfRepositories()
{
var firstPageUrl = new Uri("/repositories", UriKind.Relative);
var firstPageUrl = new Uri("/repositories?since=364", UriKind.Relative);
var secondPageUrl = new Uri("https://example.com/page/2");
var firstPageLinks = new Dictionary<string, Uri> { { "next", secondPageUrl } };
IApiResponse<List<Repository>> firstPageResponse = new ApiResponse<List<Repository>>(
@@ -195,9 +195,7 @@ namespace Octokit.Tests.Reactive
});
var gitHubClient = Substitute.For<IGitHubClient>();
gitHubClient.Connection.Get<List<Repository>>(firstPageUrl,
Arg.Is<Dictionary<string, string>>(d => d.Count == 1
&& d["since"] == "364"), null)
gitHubClient.Connection.Get<List<Repository>>(firstPageUrl, null, null)
.Returns(Task.FromResult(firstPageResponse));
gitHubClient.Connection.Get<List<Repository>>(secondPageUrl, null, null)
.Returns(Task.FromResult(secondPageResponse));
@@ -209,9 +207,7 @@ namespace Octokit.Tests.Reactive
var results = await repositoriesClient.GetAllPublic(new PublicRepositoryRequest(364)).ToArray();
Assert.Equal(7, results.Length);
gitHubClient.Connection.Received(1).Get<List<Repository>>(firstPageUrl,
Arg.Is<Dictionary<string, string>>(d=>d.Count == 1
&& d["since"] == "364"), null);
gitHubClient.Connection.Received(1).Get<List<Repository>>(firstPageUrl, null, null);
gitHubClient.Connection.Received(1).Get<List<Repository>>(secondPageUrl, null, null);
gitHubClient.Connection.Received(1).Get<List<Repository>>(thirdPageUrl, null, null);
}

View File

@@ -14,8 +14,6 @@ namespace Octokit
/// </remarks>
public class AuthorizationsClient : ApiClient, IAuthorizationsClient
{
const string previewAcceptsHeader = "application/vnd.github.mirage-preview+json";
/// <summary>
/// Initializes a new GitHub OAuth API client.
/// </summary>
@@ -38,7 +36,7 @@ namespace Octokit
/// <returns>A list of <see cref="Authorization"/>s.</returns>
public Task<IReadOnlyList<Authorization>> GetAll()
{
return ApiConnection.GetAll<Authorization>(ApiUrls.Authorizations(), null, previewAcceptsHeader);
return ApiConnection.GetAll<Authorization>(ApiUrls.Authorizations(), null);
}
/// <summary>
@@ -56,7 +54,95 @@ namespace Octokit
/// <returns>The specified <see cref="Authorization"/>.</returns>
public Task<Authorization> Get(int id)
{
return ApiConnection.Get<Authorization>(ApiUrls.Authorizations(id), null, previewAcceptsHeader);
return ApiConnection.Get<Authorization>(ApiUrls.Authorizations(id), null);
}
/// <summary>
/// Creates a new authorization for the specified OAuth application if an authorization for that application
/// doesnt already exist for the user; otherwise, it fails.
/// </summary>
/// <remarks>
/// This method requires authentication.
/// See the <a href="http://developer.github.com/v3/oauth/#get-or-create-an-authorization-for-a-specific-app">API documentation</a> for more information.
/// </remarks>
/// <param name="clientId">Client ID of the OAuth application for the token</param>
/// <param name="clientSecret">The client secret</param>
/// <param name="newAuthorization">Describes the new authorization to create</param>
/// <exception cref="AuthorizationException">
/// Thrown when the current user does not have permission to make this request.
/// </exception>
/// <exception cref="TwoFactorRequiredException">
/// Thrown when the current account has two-factor authentication enabled and an authentication code is required.
/// </exception>
/// <exception cref="ApiException">Thrown when a general API error occurs.</exception>
/// <returns>The created <see cref="Authorization"/>.</returns>
public Task<ApplicationAuthorization> Create(
string clientId,
string clientSecret,
NewAuthorization newAuthorization)
{
Ensure.ArgumentNotNullOrEmptyString(clientId, "clientId");
Ensure.ArgumentNotNullOrEmptyString(clientSecret, "clientSecret");
Ensure.ArgumentNotNull(newAuthorization, "authorization");
var requestData = new
{
client_id = clientId,
client_secret = clientSecret,
scopes = newAuthorization.Scopes,
note = newAuthorization.Note,
note_url = newAuthorization.NoteUrl,
fingerprint = newAuthorization.Fingerprint
};
var endpoint = ApiUrls.Authorizations();
return ApiConnection.Post<ApplicationAuthorization>(endpoint, requestData);
}
/// <summary>
/// Creates a new authorization for the specified OAuth application if an authorization for that application
/// doesnt already exist for the user; otherwise, it fails.
/// </summary>
/// <remarks>
/// This method requires authentication.
/// See the <a href="http://developer.github.com/v3/oauth/#get-or-create-an-authorization-for-a-specific-app">API documentation</a> for more information.
/// </remarks>
/// <param name="clientId">Client ID of the OAuth application for the token</param>
/// <param name="clientSecret">The client secret</param>
/// <param name="twoFactorAuthenticationCode">The two-factor authentication code in response to the current user's previous challenge</param>
/// <param name="newAuthorization">Describes the new authorization to create</param>
/// <exception cref="AuthorizationException">
/// Thrown when the current user does not have permission to make this request.
/// </exception>
/// <exception cref="TwoFactorRequiredException">
/// Thrown when the current account has two-factor authentication enabled and an authentication code is required.
/// </exception>
/// <exception cref="ApiException">Thrown when a general API error occurs.</exception>
/// <returns>The created <see cref="Authorization"/>.</returns>
public Task<ApplicationAuthorization> Create(
string clientId,
string clientSecret,
NewAuthorization newAuthorization,
string twoFactorAuthenticationCode)
{
Ensure.ArgumentNotNullOrEmptyString(clientId, "clientId");
Ensure.ArgumentNotNullOrEmptyString(clientSecret, "clientSecret");
Ensure.ArgumentNotNull(newAuthorization, "authorization");
Ensure.ArgumentNotNullOrEmptyString(twoFactorAuthenticationCode, "twoFactorAuthenticationCode");
var requestData = new
{
client_id = clientId,
client_secret = clientSecret,
scopes = newAuthorization.Scopes,
note = newAuthorization.Note,
note_url = newAuthorization.NoteUrl,
fingerprint = newAuthorization.Fingerprint
};
var endpoint = ApiUrls.Authorizations();
return ApiConnection.Post<ApplicationAuthorization>(endpoint, requestData, null, null, twoFactorAuthenticationCode);
}
/// <summary>
@@ -95,19 +181,12 @@ namespace Octokit
note_url = newAuthorization.NoteUrl
};
if (String.IsNullOrWhiteSpace(newAuthorization.Fingerprint))
{
// use classic API
var endpoint = ApiUrls.AuthorizationsForClient(clientId);
var endpoint = string.IsNullOrWhiteSpace(newAuthorization.Fingerprint)
? ApiUrls.AuthorizationsForClient(clientId)
: ApiUrls.AuthorizationsForClient(clientId, newAuthorization.Fingerprint);
return ApiConnection.Put<ApplicationAuthorization>(endpoint, requestData);
}
else
{
// use new API
var endpoint = ApiUrls.AuthorizationsForClient(clientId, newAuthorization.Fingerprint);
return ApiConnection.Put<ApplicationAuthorization>(endpoint, requestData, null, previewAcceptsHeader);
}
}
/// <summary>
/// Creates a new authorization for the specified OAuth application if an authorization for that application doesnt already
@@ -150,29 +229,18 @@ namespace Octokit
try
{
if (String.IsNullOrWhiteSpace(newAuthorization.Fingerprint))
{
// use classic API
var endpoint = ApiUrls.AuthorizationsForClient(clientId);
var endpoint = string.IsNullOrWhiteSpace(newAuthorization.Fingerprint)
? ApiUrls.AuthorizationsForClient(clientId)
: ApiUrls.AuthorizationsForClient(clientId, newAuthorization.Fingerprint);
return await ApiConnection.Put<ApplicationAuthorization>(
endpoint,
requestData,
twoFactorAuthenticationCode);
}
else
{
// use new API
var endpoint = ApiUrls.AuthorizationsForClient(clientId, newAuthorization.Fingerprint);
return await ApiConnection.Put<ApplicationAuthorization>(
endpoint,
requestData,
twoFactorAuthenticationCode,
previewAcceptsHeader);
}
}
catch (AuthorizationException e)
{
throw new TwoFactorChallengeFailedException(e);
throw new TwoFactorChallengeFailedException(twoFactorAuthenticationCode, e);
}
}
@@ -194,8 +262,7 @@ namespace Octokit
var endpoint = ApiUrls.ApplicationAuthorization(clientId, accessToken);
return await ApiConnection.Get<ApplicationAuthorization>(
endpoint,
null,
previewAcceptsHeader);
null);
}
/// <summary>
@@ -274,20 +341,10 @@ namespace Octokit
{
Ensure.ArgumentNotNull(authorizationUpdate, "authorizationUpdate");
if (String.IsNullOrWhiteSpace(authorizationUpdate.Fingerprint))
{
return ApiConnection.Patch<Authorization>(
ApiUrls.Authorizations(id),
authorizationUpdate);
}
else
{
return ApiConnection.Patch<Authorization>(
ApiUrls.Authorizations(id),
authorizationUpdate,
previewAcceptsHeader);
}
}
/// <summary>
/// Deletes the specified <see cref="Authorization"/>.
@@ -307,5 +364,25 @@ namespace Octokit
{
return ApiConnection.Delete(ApiUrls.Authorizations(id));
}
/// <summary>
/// Deletes the specified <see cref="Authorization"/>.
/// </summary>
/// <remarks>
/// This method requires authentication.
/// See the <a href="http://developer.github.com/v3/oauth/#delete-an-authorization">API
/// documentation</a> for more details.
/// </remarks>
/// <param name="id">The system-wide ID of the authorization to delete</param>
/// <param name="twoFactorAuthenticationCode">Two factor authorization code</param>
/// <exception cref="AuthorizationException">
/// Thrown when the current user does not have permission to make the request.
/// </exception>
/// <exception cref="ApiException">Thrown when a general API error occurs.</exception>
/// <returns>A <see cref="Task"/> for the request's execution.</returns>
public Task Delete(int id, string twoFactorAuthenticationCode)
{
return ApiConnection.Delete(ApiUrls.Authorizations(id), twoFactorAuthenticationCode);
}
}
}

View File

@@ -196,6 +196,34 @@ namespace Octokit
return ApiConnection.GetAll<Gist>(ApiUrls.UsersGists(user), request.ToParametersDictionary());
}
/// <summary>
/// List gist commits
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/gists/#list-gists-commits
/// </remarks>
/// <param name="id">The id of the gist</param>
public Task<IReadOnlyList<GistHistory>> GetAllCommits(string id)
{
Ensure.ArgumentNotNullOrEmptyString(id, "id");
return ApiConnection.GetAll<GistHistory>(ApiUrls.GistCommits(id));
}
/// <summary>
/// List gist forks
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/gists/#list-gists-forks
/// </remarks>
/// <param name="id">The id of the gist</param>
public Task<IReadOnlyList<GistFork>> GetAllForks(string id)
{
Ensure.ArgumentNotNullOrEmptyString(id, "id");
return ApiConnection.GetAll<GistFork>(ApiUrls.ForkGist(id));
}
/// <summary>
/// Edits a gist
/// </summary>

View File

@@ -49,6 +49,56 @@ namespace Octokit
Justification = "It's fiiiine. It's fine. Trust us.")]
Task<Authorization> Get(int id);
/// <summary>
/// Creates a new authorization for the specified OAuth application if an authorization for that application
/// doesnt already exist for the user; otherwise, it fails.
/// </summary>
/// <remarks>
/// This method requires authentication.
/// See the <a href="http://developer.github.com/v3/oauth/#get-or-create-an-authorization-for-a-specific-app">API documentation</a> for more information.
/// </remarks>
/// <param name="clientId">Client ID of the OAuth application for the token</param>
/// <param name="clientSecret">The client secret</param>
/// <param name="newAuthorization">Describes the new authorization to create</param>
/// <exception cref="AuthorizationException">
/// Thrown when the current user does not have permission to make this request.
/// </exception>
/// <exception cref="TwoFactorRequiredException">
/// Thrown when the current account has two-factor authentication enabled and an authentication code is required.
/// </exception>
/// <exception cref="ApiException">Thrown when a general API error occurs.</exception>
/// <returns>The created <see cref="Authorization"/>.</returns>
Task<ApplicationAuthorization> Create(
string clientId,
string clientSecret,
NewAuthorization newAuthorization);
/// <summary>
/// Creates a new authorization for the specified OAuth application if an authorization for that application
/// doesnt already exist for the user; otherwise, it fails.
/// </summary>
/// <remarks>
/// This method requires authentication.
/// See the <a href="http://developer.github.com/v3/oauth/#get-or-create-an-authorization-for-a-specific-app">API documentation</a> for more information.
/// </remarks>
/// <param name="clientId">Client ID of the OAuth application for the token</param>
/// <param name="clientSecret">The client secret</param>
/// <param name="twoFactorAuthenticationCode">The two-factor authentication code in response to the current user's previous challenge</param>
/// <param name="newAuthorization">Describes the new authorization to create</param>
/// <exception cref="AuthorizationException">
/// Thrown when the current user does not have permission to make this request.
/// </exception>
/// <exception cref="TwoFactorRequiredException">
/// Thrown when the current account has two-factor authentication enabled and an authentication code is required.
/// </exception>
/// <exception cref="ApiException">Thrown when a general API error occurs.</exception>
/// <returns>The created <see cref="Authorization"/>.</returns>
Task<ApplicationAuthorization> Create(
string clientId,
string clientSecret,
NewAuthorization newAuthorization,
string twoFactorAuthenticationCode);
/// <summary>
/// Creates a new authorization for the specified OAuth application if an authorization for that application doesnt already
/// exist for the user; otherwise, returns the users existing authorization for that application.
@@ -178,5 +228,22 @@ namespace Octokit
/// <exception cref="ApiException">Thrown when a general API error occurs.</exception>
/// <returns>A <see cref="Task"/> for the request's execution.</returns>
Task Delete(int id);
/// <summary>
/// Deletes the specified <see cref="Authorization"/>.
/// </summary>
/// <remarks>
/// This method requires authentication.
/// See the <a href="http://developer.github.com/v3/oauth/#delete-an-authorization">API
/// documentation</a> for more details.
/// </remarks>
/// <param name="id">The system-wide ID of the authorization to delete</param>
/// <param name="twoFactorAuthenticationCode">Two factor authorization code</param>
/// <exception cref="AuthorizationException">
/// Thrown when the current user does not have permission to make the request.
/// </exception>
/// <exception cref="ApiException">Thrown when a general API error occurs.</exception>
/// <returns>A <see cref="Task"/> for the request's execution.</returns>
Task Delete(int id, string twoFactorAuthenticationCode);
}
}

View File

@@ -98,6 +98,24 @@ namespace Octokit
/// <param name="since">Only gists updated at or after this time are returned</param>
Task<IReadOnlyList<Gist>> GetAllForUser(string user, DateTimeOffset since);
/// <summary>
/// List gist commits
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/gists/#list-gists-commits
/// </remarks>
/// <param name="id">The id of the gist</param>
Task<IReadOnlyList<GistHistory>> GetAllCommits(string id);
/// <summary>
/// List gist forks
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/gists/#list-gists-forks
/// </remarks>
/// <param name="id">The id of the gist</param>
Task<IReadOnlyList<GistFork>> GetAllForks(string id);
/// <summary>
/// Creates a new gist
/// </summary>

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Octokit.Internal;
namespace Octokit
{
@@ -46,6 +47,45 @@ namespace Octokit
/// <returns></returns>
Task<string> GetReadmeHtml(string owner, string name);
/// <summary>
/// This method will return a 302 to a URL to download a tarball or zipball archive for a repository.
/// Please make sure your HTTP framework is configured to follow redirects or you will need to use the
/// Location header to make a second GET request.
/// Note: For private repositories, these links are temporary and expire quickly.
/// </summary>
/// <remarks>https://developer.github.com/v3/repos/contents/#get-archive-link</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <returns></returns>
Task<string> GetArchiveLink(string owner, string name);
/// <summary>
/// This method will return a 302 to a URL to download a tarball or zipball archive for a repository.
/// Please make sure your HTTP framework is configured to follow redirects or you will need to use the
/// Location header to make a second GET request.
/// Note: For private repositories, these links are temporary and expire quickly.
/// </summary>
/// <remarks>https://developer.github.com/v3/repos/contents/#get-archive-link</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="archiveFormat">The format of the archive. Can be either tarball or zipball</param>
/// <returns></returns>
Task<string> GetArchiveLink(string owner, string name, ArchiveFormat archiveFormat);
/// <summary>
/// This method will return a 302 to a URL to download a tarball or zipball archive for a repository.
/// Please make sure your HTTP framework is configured to follow redirects or you will need to use the
/// Location header to make a second GET request.
/// Note: For private repositories, these links are temporary and expire quickly.
/// </summary>
/// <remarks>https://developer.github.com/v3/repos/contents/#get-archive-link</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="archiveFormat">The format of the archive. Can be either tarball or zipball</param>
/// <param name="reference">A valid Git reference.</param>
/// <returns></returns>
Task<string> GetArchiveLink(string owner, string name, ArchiveFormat archiveFormat, string reference);
/// <summary>
/// Creates a commit that creates a new file in a repository.
/// </summary>
@@ -75,4 +115,14 @@ namespace Octokit
/// <param name="request">Information about the file to delete</param>
Task DeleteFile(string owner, string name, string path, DeleteFileRequest request);
}
public enum ArchiveFormat
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Tarball")]
[Parameter(Value = "tarball")]
Tarball,
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Zipball")]
[Parameter(Value = "zipball")]
Zipball
}
}

View File

@@ -205,7 +205,9 @@ namespace Octokit
{
Ensure.ArgumentNotNull(request, "request");
return ApiConnection.GetAll<Repository>(ApiUrls.AllPublicRepositories(), request.ToParametersDictionary());
var url = ApiUrls.AllPublicRepositories(request.Since);
return ApiConnection.GetAll<Repository>(url);
}
/// <summary>

View File

@@ -74,6 +74,57 @@ namespace Octokit
return ApiConnection.GetHtml(ApiUrls.RepositoryReadme(owner, name), null);
}
/// <summary>
/// This method will return a 302 to a URL to download a tarball or zipball archive for a repository.
/// Please make sure your HTTP framework is configured to follow redirects or you will need to use the
/// Location header to make a second GET request.
/// Note: For private repositories, these links are temporary and expire quickly.
/// </summary>
/// <remarks>https://developer.github.com/v3/repos/contents/#get-archive-link</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <returns></returns>
public Task<string> GetArchiveLink(string owner, string name)
{
return GetArchiveLink(owner, name, ArchiveFormat.Tarball, string.Empty);
}
/// <summary>
/// This method will return a 302 to a URL to download a tarball or zipball archive for a repository.
/// Please make sure your HTTP framework is configured to follow redirects or you will need to use the
/// Location header to make a second GET request.
/// Note: For private repositories, these links are temporary and expire quickly.
/// </summary>
/// <remarks>https://developer.github.com/v3/repos/contents/#get-archive-link</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="archiveFormat">The format of the archive. Can be either tarball or zipball</param>
/// <returns></returns>
public Task<string> GetArchiveLink(string owner, string name, ArchiveFormat archiveFormat)
{
return GetArchiveLink(owner, name, archiveFormat, string.Empty);
}
/// <summary>
/// This method will return a 302 to a URL to download a tarball or zipball archive for a repository.
/// Please make sure your HTTP framework is configured to follow redirects or you will need to use the
/// Location header to make a second GET request.
/// Note: For private repositories, these links are temporary and expire quickly.
/// </summary>
/// <remarks>https://developer.github.com/v3/repos/contents/#get-archive-link</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="archiveFormat">The format of the archive. Can be either tarball or zipball</param>
/// <param name="reference">A valid Git reference.</param>
/// <returns></returns>
public Task<string> GetArchiveLink(string owner, string name, ArchiveFormat archiveFormat, string reference)
{
Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
Ensure.ArgumentNotNullOrEmptyString(name, "name");
return ApiConnection.GetRedirect(ApiUrls.RepositoryArchiveLink(owner, name, archiveFormat, reference));
}
/// <summary>
/// Creates a commit that creates a new file in a repository.
/// </summary>

View File

@@ -67,6 +67,7 @@ namespace Octokit
StatusCode = response.StatusCode;
ApiError = GetApiErrorFromExceptionMessage(response);
HttpResponse = response;
}
/// <summary>
@@ -97,6 +98,8 @@ namespace Octokit
StatusCode = statusCode;
}
public IResponse HttpResponse { get; private set; }
public override string Message
{
get { return ApiErrorMessageSafe ?? "An error occurred with this API request"; }

View File

@@ -0,0 +1,115 @@
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Runtime.Serialization;
namespace Octokit
{
#if !NETFX_CORE
/// <summary>
/// Represents a failed 2FA challenge from the API
/// </summary>
[Serializable]
#endif
[SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors",
Justification = "These exceptions are specific to the GitHub API and not general purpose exceptions")]
public abstract class TwoFactorAuthorizationException : AuthorizationException
{
/// <summary>
/// Constructs an instance of TwoFactorRequiredException.
/// </summary>
/// <param name="twoFactorType">Expected 2FA response type</param>
/// <param name="innerException">The inner exception</param>
protected TwoFactorAuthorizationException(TwoFactorType twoFactorType, Exception innerException)
: base(HttpStatusCode.Unauthorized, innerException)
{
TwoFactorType = twoFactorType;
}
/// <summary>
/// Constructs an instance of TwoFactorRequiredException.
/// </summary>
/// <param name="response">The HTTP payload from the server</param>
/// <param name="twoFactorType">Expected 2FA response type</param>
protected TwoFactorAuthorizationException(IResponse response, TwoFactorType twoFactorType)
: base(response)
{
Debug.Assert(response != null && response.StatusCode == HttpStatusCode.Unauthorized,
"TwoFactorRequiredException status code should be 401");
TwoFactorType = twoFactorType;
}
/// <summary>
/// Constructs an instance of TwoFactorRequiredException.
/// </summary>
/// <param name="response">The HTTP payload from the server</param>
/// <param name="twoFactorType">Expected 2FA response type</param>
/// <param name="innerException">The inner exception</param>
protected TwoFactorAuthorizationException(IResponse response, TwoFactorType twoFactorType, Exception innerException)
: base(response, innerException)
{
Debug.Assert(response != null && response.StatusCode == HttpStatusCode.Unauthorized,
"TwoFactorRequiredException status code should be 401");
TwoFactorType = twoFactorType;
}
/// <summary>
/// Expected 2FA response type
/// </summary>
public TwoFactorType TwoFactorType { get; private set; }
#if !NETFX_CORE
/// <summary>
/// Constructs an instance of TwoFactorRequiredException.
/// </summary>
/// <param name="info">
/// The <see cref="SerializationInfo"/> that holds the
/// serialized object data about the exception being thrown.
/// </param>
/// <param name="context">
/// The <see cref="StreamingContext"/> that contains
/// contextual information about the source or destination.
/// </param>
protected TwoFactorAuthorizationException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
if (info == null) return;
TwoFactorType = (TwoFactorType) (info.GetInt32("TwoFactorType"));
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue("TwoFactorType", TwoFactorType);
}
#endif
}
/// <summary>
/// Methods for receiving 2FA authentication codes
/// </summary>
public enum TwoFactorType
{
/// <summary>
/// No method configured
/// </summary>
None,
/// <summary>
/// Unknown method
/// </summary>
Unknown,
/// <summary>
/// Receive via SMS
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Sms")]
Sms,
/// <summary>
/// Receive via application
/// </summary>
AuthenticatorApp
}
}

View File

@@ -1,6 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Runtime.Serialization;
namespace Octokit
@@ -13,23 +12,26 @@ namespace Octokit
#endif
[SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors",
Justification = "These exceptions are specific to the GitHub API and not general purpose exceptions")]
public class TwoFactorChallengeFailedException : AuthorizationException
public class TwoFactorChallengeFailedException : TwoFactorAuthorizationException
{
/// <summary>
/// Constructs an instance of TwoFactorChallengeFailedException
/// </summary>
public TwoFactorChallengeFailedException() :
base(HttpStatusCode.Unauthorized, null)
public TwoFactorChallengeFailedException() : base(TwoFactorType.None, null)
{
}
/// <summary>
/// Constructs an instance of TwoFactorChallengeFailedException
/// </summary>
/// <param name="authorizationCode">The authorization code that was incorrect</param>
/// <param name="innerException">The inner exception</param>
public TwoFactorChallengeFailedException(Exception innerException)
: base(HttpStatusCode.Unauthorized, innerException)
public TwoFactorChallengeFailedException(
string authorizationCode,
ApiException innerException)
: base(ParseTwoFactorType(innerException), innerException)
{
AuthorizationCode = authorizationCode;
}
public override string Message
@@ -37,9 +39,16 @@ namespace Octokit
get { return "The two-factor authentication code supplied is not correct"; }
}
public string AuthorizationCode { get; private set; }
static TwoFactorType ParseTwoFactorType(ApiException exception)
{
return exception == null ? TwoFactorType.None : Connection.ParseTwoFactorType(exception.HttpResponse);
}
#if !NETFX_CORE
/// <summary>
/// Constructs an instance of TwoFactorChallengeFailedException
/// Constructs an instance of TwoFactorChallengeFailedException.
/// </summary>
/// <param name="info">
/// The <see cref="SerializationInfo"/> that holds the
@@ -52,6 +61,14 @@ namespace Octokit
protected TwoFactorChallengeFailedException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
if (info == null) return;
AuthorizationCode = info.GetString("AuthorizationCode");
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue("AuthorizationCode", AuthorizationCode);
}
#endif
}

View File

@@ -14,7 +14,7 @@ namespace Octokit
#endif
[SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors",
Justification = "These exceptions are specific to the GitHub API and not general purpose exceptions")]
public class TwoFactorRequiredException : AuthorizationException
public class TwoFactorRequiredException : TwoFactorAuthorizationException
{
/// <summary>
/// Constructs an instance of TwoFactorRequiredException.
@@ -27,10 +27,8 @@ namespace Octokit
/// Constructs an instance of TwoFactorRequiredException.
/// </summary>
/// <param name="twoFactorType">Expected 2FA response type</param>
public TwoFactorRequiredException(TwoFactorType twoFactorType)
: base(HttpStatusCode.Unauthorized, null)
public TwoFactorRequiredException(TwoFactorType twoFactorType) : base(twoFactorType, null)
{
TwoFactorType = twoFactorType;
}
/// <summary>
@@ -38,12 +36,11 @@ namespace Octokit
/// </summary>
/// <param name="response">The HTTP payload from the server</param>
/// <param name="twoFactorType">Expected 2FA response type</param>
public TwoFactorRequiredException(IResponse response, TwoFactorType twoFactorType) : base(response)
public TwoFactorRequiredException(IResponse response, TwoFactorType twoFactorType)
: base(response, twoFactorType)
{
Debug.Assert(response != null && response.StatusCode == HttpStatusCode.Unauthorized,
"TwoFactorRequiredException status code should be 401");
TwoFactorType = twoFactorType;
}
public override string Message
@@ -66,44 +63,7 @@ namespace Octokit
protected TwoFactorRequiredException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
if (info == null) return;
TwoFactorType = (TwoFactorType) (info.GetInt32("TwoFactorType"));
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue("TwoFactorType", TwoFactorType);
}
#endif
/// <summary>
/// Expected 2FA response type
/// </summary>
public TwoFactorType TwoFactorType { get; private set; }
}
/// <summary>
/// Methods for receiving 2FA authentication codes
/// </summary>
public enum TwoFactorType
{
/// <summary>
/// No method configured
/// </summary>
None,
/// <summary>
/// Unknown method
/// </summary>
Unknown,
/// <summary>
/// Receive via SMS
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Sms")]
Sms,
/// <summary>
/// Receive via application
/// </summary>
AuthenticatorApp
}
}

View File

@@ -23,7 +23,7 @@ namespace Octokit
/// the user agent for analytics purposes.
/// </param>
public GitHubClient(ProductHeaderValue productInformation)
: this(new Connection(productInformation))
: this(new Connection(productInformation, GitHubApiUrl))
{
}

View File

@@ -90,6 +90,14 @@ namespace Octokit
return connection.Get<T>(uri, null, null);
}
public static Task<IApiResponse<T>> GetRedirect<T>(this IConnection connection, Uri uri)
{
Ensure.ArgumentNotNull(connection, "connection");
Ensure.ArgumentNotNull(uri, "uri");
return connection.Get<T>(uri, null, null, false);
}
/// <summary>
/// Gets the API resource at the specified URI.
/// </summary>

View File

@@ -19,15 +19,23 @@ namespace Octokit
static readonly Uri _oauthAuthorize = new Uri("login/oauth/authorize", UriKind.Relative);
static readonly Uri _oauthAccesToken = new Uri("login/oauth/access_token", UriKind.Relative);
/// <summary>
/// Returns the <see cref="Uri"/> that returns all public repositories in
/// response to a GET request.
/// </summary>
public static Uri AllPublicRepositories()
{
return "/repositories".FormatUri();
}
/// <summary>
/// Returns the <see cref="Uri"/> that returns all public repositories in
/// response to a GET request.
/// </summary>
/// <returns></returns>
public static Uri AllPublicRepositories()
/// <param name="since">The integer ID of the last Repository that youve seen.</param>
public static Uri AllPublicRepositories(long since)
{
return "/repositories".FormatUri();
return "/repositories?since={0}".FormatUri(since);
}
/// <summary>
@@ -773,7 +781,7 @@ namespace Octokit
}
/// <summary>
/// Returns the <see cref="Uri"/> for the forks of a given gist.
/// Returns the <see cref="Uri"/> for the forks for the specified gist.
/// </summary>
/// <param name="id">The id of the gist</param>
public static Uri ForkGist(string id)
@@ -824,6 +832,15 @@ namespace Octokit
return "gists/{0}/comments".FormatUri(gistId);
}
/// <summary>
/// Returns the <see cref="Uri"/> for the commits for the specified gist.
/// </summary>
/// <param name="id">The id of the gist</param>
public static Uri GistCommits(string id)
{
return "gists/{0}/commits".FormatUri(id);
}
/// <summary>
/// Returns the <see cref="Uri"/> that returns the specified pull request.
/// </summary>
@@ -1508,5 +1525,10 @@ namespace Octokit
{
return "repos/{0}/{1}/contents/{2}".FormatUri(owner, name, path);
}
public static Uri RepositoryArchiveLink(string owner, string name, ArchiveFormat archiveFormat, string reference)
{
return "repos/{0}/{1}/{2}/{3}".FormatUri(owner, name, archiveFormat.ToParameter(), reference);
}
}
}

View File

@@ -199,6 +199,33 @@ namespace Octokit
return response.Body;
}
/// <summary>
/// Creates a new API resource in the list at the specified URI.
/// </summary>
/// <typeparam name="T">The API resource's type.</typeparam>
/// <param name="uri">URI of the API resource to get</param>
/// <param name="data">Object that describes the new API resource; this will be serialized and used as the request's body</param>
/// <param name="accepts">Accept header to use for the API request</param>
/// <param name="contentType">Content type of the API request</param>
/// <param name="twoFactorAuthenticationCode">Two Factor Authentication Code</param>
/// <returns>The created API resource.</returns>
/// <exception cref="ApiException">Thrown when an API error occurs.</exception>
public async Task<T> Post<T>(Uri uri, object data, string accepts, string contentType, string twoFactorAuthenticationCode)
{
Ensure.ArgumentNotNull(uri, "uri");
Ensure.ArgumentNotNull(data, "data");
Ensure.ArgumentNotNull(twoFactorAuthenticationCode, "twoFactorAuthenticationCode");
var response = await Connection.Post<T>(
uri,
data,
accepts,
contentType,
twoFactorAuthenticationCode).ConfigureAwait(false);
return response.Body;
}
public async Task<T> Post<T>(Uri uri, object data, string accepts, string contentType, TimeSpan timeout)
{
Ensure.ArgumentNotNull(uri, "uri");
@@ -345,6 +372,19 @@ namespace Octokit
return Connection.Delete(uri);
}
/// <summary>
/// Deletes the API object at the specified URI.
/// </summary>
/// <param name="uri">URI of the API resource to delete</param>
/// <param name="twoFactorAuthenticationCode">Two Factor Code</param>
/// <returns>A <see cref="Task"/> for the request's execution.</returns>
public Task Delete(Uri uri, string twoFactorAuthenticationCode)
{
Ensure.ArgumentNotNull(uri, "uri");
return Connection.Delete(uri, twoFactorAuthenticationCode);
}
/// <summary>
/// Deletes the API object at the specified URI.
/// </summary>
@@ -359,6 +399,28 @@ namespace Octokit
return Connection.Delete(uri, data);
}
/// <summary>
/// Executes a GET to the API object at the specified URI. This operation is appropriate for
/// API calls which wants to return the redirect URL.
/// It expects the API to respond with a 302 Found.
/// </summary>
/// <param name="uri">URI of the API resource to get</param>
/// <returns>The URL returned by the API in the Location header</returns>
/// <exception cref="ApiException">Thrown when an API error occurs, or the API does not respond with a 302 Found</exception>
public async Task<string> GetRedirect(Uri uri)
{
Ensure.ArgumentNotNull(uri, "uri");
var response = await Connection.GetRedirect<string>(uri);
if (response.HttpResponse.StatusCode == HttpStatusCode.Redirect)
{
return response.HttpResponse.Headers["Location"];
}
throw new ApiException("Redirect Operation expect status code of Redirect.",
response.HttpResponse.StatusCode);
}
/// <summary>
/// Executes a GET to the API object at the specified URI. This operation is appropriate for
/// API calls which queue long running calculations.

View File

@@ -142,6 +142,24 @@ namespace Octokit
return SendData<T>(uri.ApplyParameters(parameters), HttpMethod.Get, null, accepts, null, CancellationToken.None);
}
/// <summary>
/// Performs an asynchronous HTTP GET request.
/// Attempts to map the response to an object of type <typeparamref name="T"/>
/// </summary>
/// <typeparam name="T">The type to map the response to</typeparam>
/// <param name="uri">URI endpoint to send request to</param>
/// <param name="parameters">Querystring parameters for the request</param>
/// <param name="accepts">Specifies accepted response media types.</param>
/// <param name="allowAutoRedirect">To follow redirect links automatically or not</param>
/// <returns><seealso cref="IResponse"/> representing the received HTTP response</returns>
public Task<IApiResponse<T>> Get<T>(Uri uri, IDictionary<string, string> parameters, string accepts, bool allowAutoRedirect)
{
Ensure.ArgumentNotNull(uri, "uri");
return SendData<T>(uri.ApplyParameters(parameters), HttpMethod.Get, null, accepts, null, CancellationToken.None, allowAutoRedirect: allowAutoRedirect);
}
public Task<IApiResponse<T>> Get<T>(Uri uri, IDictionary<string, string> parameters, string accepts, CancellationToken cancellationToken)
{
Ensure.ArgumentNotNull(uri, "uri");
@@ -205,6 +223,27 @@ namespace Octokit
return SendData<T>(uri, HttpMethod.Post, body, accepts, contentType, CancellationToken.None);
}
/// <summary>
/// Performs an asynchronous HTTP POST request.
/// Attempts to map the response body to an object of type <typeparamref name="T"/>
/// </summary>
/// <typeparam name="T">The type to map the response to</typeparam>
/// <param name="uri">URI endpoint to send request to</param>
/// <param name="body">The object to serialize as the body of the request</param>
/// <param name="accepts">Specifies accepted response media types.</param>
/// <param name="contentType">Specifies the media type of the request body</param>
/// <param name="twoFactorAuthenticationCode">Two Factor Authentication Code</param>
/// <returns><seealso cref="IResponse"/> representing the received HTTP response</returns>
public Task<IApiResponse<T>> Post<T>(Uri uri, object body, string accepts, string contentType, string twoFactorAuthenticationCode)
{
Ensure.ArgumentNotNull(uri, "uri");
Ensure.ArgumentNotNull(body, "body");
Ensure.ArgumentNotNullOrEmptyString(twoFactorAuthenticationCode, "twoFactorAuthenticationCode");
return SendData<T>(uri, HttpMethod.Post, body, accepts, contentType, CancellationToken.None, twoFactorAuthenticationCode);
}
public Task<IApiResponse<T>> Post<T>(Uri uri, object body, string accepts, string contentType, TimeSpan timeout)
{
Ensure.ArgumentNotNull(uri, "uri");
@@ -281,7 +320,8 @@ namespace Octokit
string contentType,
CancellationToken cancellationToken,
string twoFactorAuthenticationCode = null,
Uri baseAddress = null)
Uri baseAddress = null,
bool allowAutoRedirect = true)
{
Ensure.ArgumentNotNull(uri, "uri");
@@ -290,6 +330,7 @@ namespace Octokit
Method = method,
BaseAddress = baseAddress ?? BaseAddress,
Endpoint = uri,
AllowAutoRedirect = allowAutoRedirect,
};
return SendDataInternal<T>(body, accepts, contentType, cancellationToken, twoFactorAuthenticationCode, request);
@@ -374,6 +415,27 @@ namespace Octokit
return response.HttpResponse.StatusCode;
}
/// <summary>
/// Performs an asynchronous HTTP DELETE request that expects an empty response.
/// </summary>
/// <param name="uri">URI endpoint to send request to</param>
/// <param name="twoFactorAuthenticationCode">Two Factor Code</param>
/// <returns>The returned <seealso cref="HttpStatusCode"/></returns>
public async Task<HttpStatusCode> Delete(Uri uri, string twoFactorAuthenticationCode)
{
Ensure.ArgumentNotNull(uri, "uri");
var response = await SendData<object>(
uri,
HttpMethod.Delete,
null,
null,
null,
CancellationToken.None,
twoFactorAuthenticationCode);
return response.HttpResponse.StatusCode;
}
/// <summary>
/// Performs an asynchronous HTTP DELETE request that expects an empty response.
/// </summary>
@@ -502,9 +564,9 @@ namespace Octokit
: new ForbiddenException(response);
}
static TwoFactorType ParseTwoFactorType(IResponse restResponse)
internal static TwoFactorType ParseTwoFactorType(IResponse restResponse)
{
if (restResponse.Headers == null || !restResponse.Headers.Any()) return TwoFactorType.None;
if (restResponse == null || restResponse.Headers == null || !restResponse.Headers.Any()) return TwoFactorType.None;
var otpHeader = restResponse.Headers.FirstOrDefault(header =>
header.Key.Equals("X-GitHub-OTP", StringComparison.OrdinalIgnoreCase));
if (String.IsNullOrEmpty(otpHeader.Value)) return TwoFactorType.None;

View File

@@ -50,15 +50,21 @@ namespace Octokit.Internal
httpOptions.Proxy = _webProxy;
}
var http = new HttpClient(httpOptions)
var http = new HttpClient(httpOptions);
var cancellationTokenForRequest = cancellationToken;
if (request.Timeout != TimeSpan.Zero)
{
BaseAddress = request.BaseAddress,
Timeout = request.Timeout
};
var timeoutCancellation = new CancellationTokenSource(request.Timeout);
var unifiedCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCancellation.Token);
cancellationTokenForRequest = unifiedCancellationToken.Token;
}
using (var requestMessage = BuildRequestMessage(request))
{
// Make the request
var responseMessage = await http.SendAsync(requestMessage, HttpCompletionOption.ResponseContentRead, cancellationToken)
var responseMessage = await http.SendAsync(requestMessage, HttpCompletionOption.ResponseContentRead, cancellationTokenForRequest)
.ConfigureAwait(false);
return await BuildResponse(responseMessage).ConfigureAwait(false);
}
@@ -76,14 +82,14 @@ namespace Octokit.Internal
{
contentType = GetContentMediaType(responseMessage.Content);
// We added support for downloading images. Let's constrain this appropriately.
if (contentType == null || !contentType.StartsWith("image/"))
// We added support for downloading images and zip-files. Let's constrain this appropriately.
if (contentType != null && (contentType.StartsWith("image/") || contentType.Equals("application/zip", StringComparison.OrdinalIgnoreCase)))
{
responseBody = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
responseBody = await responseMessage.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
}
else
{
responseBody = await responseMessage.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
responseBody = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
}
}
}
@@ -101,7 +107,8 @@ namespace Octokit.Internal
HttpRequestMessage requestMessage = null;
try
{
requestMessage = new HttpRequestMessage(request.Method, request.Endpoint);
var fullUri = new Uri(request.BaseAddress, request.Endpoint);
requestMessage = new HttpRequestMessage(request.Method, fullUri);
foreach (var header in request.Headers)
{
requestMessage.Headers.Add(header.Key, header.Value);

View File

@@ -123,6 +123,19 @@ namespace Octokit
/// <exception cref="ApiException">Thrown when an API error occurs.</exception>
Task<T> Post<T>(Uri uri, object data, string accepts, string contentType);
/// <summary>
/// Creates a new API resource in the list at the specified URI.
/// </summary>
/// <typeparam name="T">The API resource's type.</typeparam>
/// <param name="uri">URI of the API resource to get</param>
/// <param name="data">Object that describes the new API resource; this will be serialized and used as the request's body</param>
/// <param name="accepts">Accept header to use for the API request</param>
/// <param name="contentType">Content type of the API request</param>
/// <param name="twoFactorAuthenticationCode">Two Factor Authentication Code</param>
/// <returns>The created API resource.</returns>
/// <exception cref="ApiException">Thrown when an API error occurs.</exception>
Task<T> Post<T>(Uri uri, object data, string accepts, string contentType, string twoFactorAuthenticationCode);
/// <summary>
/// Creates a new API resource in the list at the specified URI.
/// </summary>
@@ -212,6 +225,14 @@ namespace Octokit
/// <returns>A <see cref="Task"/> for the request's execution.</returns>
Task Delete(Uri uri);
/// <summary>
/// Deletes the API object at the specified URI.
/// </summary>
/// <param name="uri">URI of the API resource to delete</param>
/// <param name="twoFactorAuthenticationCode">Two Factor Code</param>
/// <returns>A <see cref="Task"/> for the request's execution.</returns>
Task Delete(Uri uri, string twoFactorAuthenticationCode);
/// <summary>
/// Deletes the API object at the specified URI.
/// </summary>
@@ -220,6 +241,16 @@ namespace Octokit
/// <returns>A <see cref="Task"/> for the request's execution.</returns>
Task Delete(Uri uri, object data);
/// <summary>
/// Executes a GET to the API object at the specified URI. This operation is appropriate for
/// API calls which wants to return the redirect URL.
/// It expects the API to respond with a 302 Found.
/// </summary>
/// <param name="uri">URI of the API resource to get</param>
/// <returns>The URL returned by the API in the Location header</returns>
/// <exception cref="ApiException">Thrown when an API error occurs, or the API does not respond with a 302 Found</exception>
Task<string> GetRedirect(Uri uri);
/// <summary>
/// Executes a GET to the API object at the specified URI. This operation is appropriate for
/// API calls which queue long running calculations.

View File

@@ -32,6 +32,19 @@ namespace Octokit
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get")]
Task<IApiResponse<T>> Get<T>(Uri uri, IDictionary<string, string> parameters, string accepts);
/// <summary>
/// Performs an asynchronous HTTP GET request.
/// Attempts to map the response to an object of type <typeparamref name="T"/>
/// </summary>
/// <typeparam name="T">The type to map the response to</typeparam>
/// <param name="uri">URI endpoint to send request to</param>
/// <param name="parameters">Querystring parameters for the request</param>
/// <param name="accepts">Specifies accepted response media types.</param>
/// <param name="allowAutoRedirect">To follow redirect links automatically or not</param>
/// <returns><seealso cref="IResponse"/> representing the received HTTP response</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get")]
Task<IApiResponse<T>> Get<T>(Uri uri, IDictionary<string, string> parameters, string accepts, bool allowAutoRedirect);
/// <summary>
/// Performs an asynchronous HTTP GET request.
/// Attempts to map the response to an object of type <typeparamref name="T"/>
@@ -92,6 +105,19 @@ namespace Octokit
/// <returns><seealso cref="IResponse"/> representing the received HTTP response</returns>
Task<IApiResponse<T>> Post<T>(Uri uri, object body, string accepts, string contentType);
/// <summary>
/// Performs an asynchronous HTTP POST request.
/// Attempts to map the response body to an object of type <typeparamref name="T"/>
/// </summary>
/// <typeparam name="T">The type to map the response to</typeparam>
/// <param name="uri">URI endpoint to send request to</param>
/// <param name="body">The object to serialize as the body of the request</param>
/// <param name="accepts">Specifies accepted response media types.</param>
/// <param name="contentType">Specifies the media type of the request body</param>
/// <param name="twoFactorAuthenticationCode">Two Factor Authentication Code</param>
/// <returns><seealso cref="IResponse"/> representing the received HTTP response</returns>
Task<IApiResponse<T>> Post<T>(Uri uri, object body, string accepts, string contentType, string twoFactorAuthenticationCode);
/// <summary>
/// Performs an asynchronous HTTP POST request.
/// Attempts to map the response body to an object of type <typeparamref name="T"/>
@@ -171,6 +197,14 @@ namespace Octokit
/// <returns>The returned <seealso cref="HttpStatusCode"/></returns>
Task<HttpStatusCode> Delete(Uri uri);
/// <summary>
/// Performs an asynchronous HTTP DELETE request that expects an empty response.
/// </summary>
/// <param name="uri">URI endpoint to send request to</param>
/// <param name="twoFactorAuthenticationCode">Two Factor Code</param>
/// <returns>The returned <seealso cref="HttpStatusCode"/></returns>
Task<HttpStatusCode> Delete(Uri uri, string twoFactorAuthenticationCode);
/// <summary>
/// Performs an asynchronous HTTP DELETE request that expects an empty response.
/// </summary>

View File

@@ -56,7 +56,7 @@ namespace Octokit
/// <summary>
/// Line index in the diff that was commented on.
/// </summary>
public int Position { get; protected set; }
public int? Position { get; protected set; }
/// <summary>
/// The line number in the file that was commented on.

View File

@@ -5,7 +5,7 @@ using System.Globalization;
namespace Octokit
{
/// <summary>
/// User by <see cref="GistHistory"/> to indicate the level of change.
/// Used by <see cref="GistHistory"/> to indicate the level of change.
/// </summary>
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class GistChangeStatus

View File

@@ -14,7 +14,7 @@ namespace Octokit
{
public PullRequestFile() { }
public PullRequestFile(string sha, string fileName, string status, int additions, int deletions, int changes, Uri blobUri, Uri rawUri, Uri contentsUri, string patch)
public PullRequestFile(string sha, string fileName, string status, int additions, int deletions, int changes, Uri blobUrl, Uri rawUrl, Uri contentsUrl, string patch)
{
Sha = sha;
FileName = fileName;
@@ -22,9 +22,9 @@ namespace Octokit
Additions = additions;
Deletions = deletions;
Changes = changes;
BlobUri = blobUri;
RawUri = rawUri;
ContentsUri = contentsUri;
BlobUrl = blobUrl;
RawUrl = rawUrl;
ContentsUrl = contentsUrl;
Patch = patch;
}
@@ -35,9 +35,9 @@ namespace Octokit
public int Additions { get; protected set; }
public int Deletions { get; protected set; }
public int Changes { get; protected set; }
public Uri BlobUri { get; protected set; }
public Uri RawUri { get; protected set; }
public Uri ContentsUri { get; protected set; }
public Uri BlobUrl { get; protected set; }
public Uri RawUrl { get; protected set; }
public Uri ContentsUrl { get; protected set; }
public string Patch { get; protected set; }
internal string DebuggerDisplay

View File

@@ -392,6 +392,7 @@
<Compile Include="Models\Response\LicenseMetadata.cs" />
<Compile Include="Models\Response\PullRequestFile.cs" />
<Compile Include="Models\Request\PublicRepositoryRequest.cs" />
<Compile Include="Exceptions\TwoFactorAuthorizationException.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -407,6 +407,7 @@
<Compile Include="Models\Response\PullRequestFile.cs" />
<Compile Include="Models\Request\PublicRepositoryRequest.cs" />
<Compile Include="Models\Request\RepositoryHooksPingRequest.cs" />
<Compile Include="Exceptions\TwoFactorAuthorizationException.cs" />
<Compile Include="Models\Request\RepositoryHookTestRequest.cs" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Novell\Novell.MonoDroid.CSharp.targets" />

View File

@@ -400,6 +400,7 @@
<Compile Include="Models\Response\PullRequestFile.cs" />
<Compile Include="Models\Request\PublicRepositoryRequest.cs" />
<Compile Include="Models\Request\RepositoryHooksPingRequest.cs" />
<Compile Include="Exceptions\TwoFactorAuthorizationException.cs" />
<Compile Include="Models\Request\RepositoryHookTestRequest.cs" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.MonoTouch.CSharp.targets" />

View File

@@ -390,6 +390,7 @@
<Compile Include="Models\Response\LicenseMetadata.cs" />
<Compile Include="Models\Response\PullRequestFile.cs" />
<Compile Include="Models\Request\PublicRepositoryRequest.cs" />
<Compile Include="Exceptions\TwoFactorAuthorizationException.cs" />
</ItemGroup>
<ItemGroup>
<CodeAnalysisDictionary Include="..\CustomDictionary.xml">

View File

@@ -394,6 +394,7 @@
<Compile Include="Models\Response\LicenseMetadata.cs" />
<Compile Include="Models\Response\PullRequestFile.cs" />
<Compile Include="Models\Request\PublicRepositoryRequest.cs" />
<Compile Include="Exceptions\TwoFactorAuthorizationException.cs" />
</ItemGroup>
<ItemGroup>
<CodeAnalysisDictionary Include="..\CustomDictionary.xml">

View File

@@ -73,6 +73,7 @@
<Compile Include="Clients\RepositoryContentsClient.cs" />
<Compile Include="Exceptions\PrivateRepositoryQuotaExceededException.cs" />
<Compile Include="Exceptions\RepositoryExistsException.cs" />
<Compile Include="Exceptions\TwoFactorAuthorizationException.cs" />
<Compile Include="Helpers\ApiErrorExtensions.cs" />
<Compile Include="Helpers\ApiUrls.Authorizations.cs" />
<Compile Include="Helpers\ApiUrls.Keys.cs" />

View File

@@ -1,3 +1,21 @@
### New in 0.11.0 (released 2015/05/10)
* New: Added overload to `IRepositoryClient.GetAllPublic` specifying a `since` parameter - #774 via @alfhenrik
* New: Added `IGistsClient.GetAllCommits` and `IGistsClient.GetAllForks` implementations - #542 via @haagenson, #794 via @shiftkey
* New: Added `IRepositoryContentsClient.GetArchiveLink` for getting archived code - #765 via @alfhenrik
* Fixed: `PullRequestFile` properties were not serialized correctly - #789 via @thedillonb
* Fixed: Allow to download zip-attachments - #792 via @csware
### New in 0.10.0 (released 2015/04/22)
* Fixed: renamed methods to follow `GetAll` convention - #771 via @alfhenrik
* Fixed: helper functions and cleanup to make using Authorization API easier to consume - #786 via @haacked
**Breaking Changes:**
- As part of #771 there were many method which were returning collections
but the method name made it unclear. You might think that it wasn't much, but
you'd be wrong. So if you have a method that no longer compile,
it is likely that you need to set the prefix to `GetAll` to re-disocver that API.
- `CommitComment.Position` is now a nullable `int` to prevent serialization issues.
### New in 0.9.0 (released 2015/04/04)
* New: added `PullRequest.Files` APIs - #752 via @alfhenrik
* Fixed: `PullRequestRequest` now supports `SortDirection` and `SortProperty` - #752 via @alfhenrik

View File

@@ -3,11 +3,11 @@ using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyProductAttribute("Octokit")]
[assembly: AssemblyVersionAttribute("0.9.0")]
[assembly: AssemblyFileVersionAttribute("0.9.0")]
[assembly: AssemblyVersionAttribute("0.11.0")]
[assembly: AssemblyFileVersionAttribute("0.11.0")]
[assembly: ComVisibleAttribute(false)]
namespace System {
internal static class AssemblyVersionInformation {
internal const string Version = "0.9.0";
internal const string Version = "0.11.0";
}
}