mirror of
https://github.com/zoriya/octokit.net.git
synced 2025-12-06 07:16:09 +00:00
Adding initial support for GitHub Apps. (#1738)
* Added authentication using bearer token. * Added Installation and AccessToken clients. * Added new clients to Reactive project * added support for DateTime serialized as FileTime * added support for StatusEventPayload * added support for StatusEventPayload * Added test for StatusEventPayload and fixed serializer to return that event payload type. * WIP - added ApplicationClient and related Api Urls. * Continued implementing Installations support. * Fixing build (WIP) * fixed build * added Account property to Installation. prefer nameof(x) over literal "x". * fixed according to code review. * fixed build. * switched Installation ID from int to long. * added Permissions and Events properties to Installation. * added documentation to Application and Installation properties in IGitHubClient. * wip - added tests to new clients * wip - fix build * wip - fixed build. * added InstallationsClient tests. * added integration test for InstallationsClient. * changes requested in code review. * add Get method for App * Create GitHubApp response model instead of re-using existing Application response model * add Get method to observable client * fixed build (both locally and failed test). * Fixed documentation and added some missing XML docs. * added DebuggerDisplay to StatusEventPayload * updated XML docs and added some missing bits. prefer nameof(x) over literal "x". * Add xml comments to AccessToken response model and use DateTimeOffset rather than DateTime * Tidy up XmlComments and make consistent across client and observable client and interfaces * fixup unit tests to independently verify preview header * Implement GetInstallation method * revert commits unrelated to GitHubApps - these can be done on a separate PR if required * this extra authenticator class doesnt appear to be used anywhere * undo project file change as it doesnt appear to be necessary * Revert "Merge remote-tracking branch 'remote/GitHubApps' into GitHubApps" This reverts commit c53cc110b8d807f62fdfeaa7df19e1532d050007, reversing changes made to 0c9e413d420a4725738644ea5b13af6ec102d456. * Revert "Revert "Merge remote-tracking branch 'remote/GitHubApps' into GitHubApps"" This reverts commit 02d52b8adf814b6945c60cb59a907a8cd34b1ce7. * add XmlDoc comments to response models and flesh out installation permissions * name AcceptHeaders member consistently * accidentally lost changes to Credentials.cs * Enhance Intergation test framework to handle GitHubApp settings and discoer tests appropriately Get code ready for GitHubJWT nuget package but for now just hardcode a JWT in ENV VAR Add 1 integration test for each method and ensure they are working! * fixed compiler warnings. * Added support for Installation=>Id field that arrives in a Pull Request Event payload. (See the last field in the sample JSON of https://developer.github.com/v3/activity/events/types/#pullrequestevent) * Change integration test project to netcoreapp2.0 so we can use GitHubJwt nuget package in integration tests * First cut at some GitHubApp doco * update mkdocs config * Moved the Installation property to ActivityPayload, so it's available in all payloads. This feature is not undocumented, unfortunately, but valid: https://platform.github.community/t/determining-which-installation-an-event-came-from/539/11 * Split Installation to Installation and InstallationId, and added a comfort method for gaining its AccessToken. * fixed InstallationId CreateAccessToken to receive IGitHubAppsClient. added (and fixed) docs. * reverted object-oriented style comfort method and it's docs. * update all test projects to netcoreapp2.0 * tweak build configs to use 2.0.3 SDK * also need to update cake frosting build to netcoreapp2.0 * tweak docs some more * fix convention test failures * test projects still had some old runtime parts in them! * travis osx image needs to be at least 10.12 for .NET Core 2.0 * shell script might need the same argument tweak for cake * more doc tweaks * Make sure compiler warning output isnt somehow causing Linux and OSX builds to fail * moar logging for linux/OSX builds * stop sourcelink on linux/OSX builds to see if that is the problem * set verbosity to detailed for the dotnet build step * try new sourcelink and list out remotes * is travis being weird with git clone? * SourceLink may be defaulting to true on CI server so explicitly set it as false rather than omitting it * detailed is a bit too verbose for travis, try normal * turn sourcelink back on for Linux/OSX * fix compiler warning * Try SourceLink.Create.CommandLine instead of SourceLink.Create.GitHub * CliToolReferences did not update to latest versions * remove debug origin info * turn off msbuild output * go back to SourceLink.Create.GitHub! * time diff between dev PC and API causes issues if specifying a full 600 second token * handle extra date format that Installation end point now returns * field needs to be protected in order to be deserialized * provide even more buffer for client vs server clock drift * Update to latest GitHubJwt reference * go back to SDK 1 since SDK 2 is having sporadic travisCI faliures in TestSourceLink build step * get appveyor working * update sourcelink back to latest, and use SDK 1.04 (runtime 1.0.5)
This commit is contained in:
committed by
Ryan Gribble
parent
7aeea34fb2
commit
5ffc96995f
@@ -7,7 +7,7 @@ matrix:
|
||||
sudo: required
|
||||
dotnet: 1.0.4
|
||||
- os: osx
|
||||
osx_image: xcode8
|
||||
osx_image: xcode8.3
|
||||
dotnet: 1.0.4
|
||||
|
||||
before_script:
|
||||
@@ -15,5 +15,4 @@ before_script:
|
||||
|
||||
script:
|
||||
- git fetch --unshallow --tags
|
||||
- dotnet --info
|
||||
- ./build.sh --linksources=true
|
||||
- ./build.sh --linksources=true --verbosity=verbose
|
||||
|
||||
57
Octokit.Reactive/Clients/IObservableGitHubAppsClient.cs
Normal file
57
Octokit.Reactive/Clients/IObservableGitHubAppsClient.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
|
||||
namespace Octokit.Reactive
|
||||
{
|
||||
/// <summary>
|
||||
/// A client for GitHub Applications API. Provides the methods required to get GitHub applications and installations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See the <a href="http://developer.github.com/v3/apps/">GitHub Apps API documentation</a> for more information.
|
||||
/// </remarks>
|
||||
public interface IObservableGitHubAppsClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a single GitHub App.
|
||||
/// </summary>
|
||||
/// <remarks>https://developer.github.com/v3/apps/#get-a-single-github-app</remarks>
|
||||
/// <param name="slug">The URL-friendly name of your GitHub App. You can find this on the settings page for your GitHub App.</param>
|
||||
IObservable<GitHubApp> Get(string slug);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the GitHub App associated with the authentication credentials used (requires GitHubApp JWT token auth).
|
||||
/// </summary>
|
||||
/// <remarks>https://developer.github.com/v3/apps/#get-the-authenticated-github-app</remarks>
|
||||
IObservable<GitHubApp> GetCurrent();
|
||||
|
||||
/// <summary>
|
||||
/// List installations of the authenticated GitHub App (requires GitHubApp JWT token auth).
|
||||
/// </summary>
|
||||
/// <remarks>https://developer.github.com/v3/apps/#find-installations</remarks>
|
||||
IObservable<Installation> GetAllInstallationsForCurrent();
|
||||
|
||||
/// <summary>
|
||||
/// List installations of the authenticated GitHub App (requires GitHubApp JWT token auth).
|
||||
/// </summary>
|
||||
/// <param name="options">Options for changing the API response</param>
|
||||
/// <remarks>https://developer.github.com/v3/apps/#find-installations</remarks>
|
||||
IObservable<Installation> GetAllInstallationsForCurrent(ApiOptions options);
|
||||
|
||||
/// <summary>
|
||||
/// Get a single GitHub App Installation (requires GitHubApp JWT token auth).
|
||||
/// </summary>
|
||||
/// <remarks>https://developer.github.com/v3/apps/#get-a-single-installation</remarks>
|
||||
/// <param name="installationId">The Id of the GitHub App Installation</param>
|
||||
IObservable<Installation> GetInstallation(long installationId);
|
||||
|
||||
/// <summary>
|
||||
/// Create a time bound access token for a GitHubApp Installation that can be used to access other API endpoints (requires GitHubApp JWT token auth).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// https://developer.github.com/v3/apps/#create-a-new-installation-token
|
||||
/// https://developer.github.com/apps/building-github-apps/authentication-options-for-github-apps/#authenticating-as-an-installation
|
||||
/// https://developer.github.com/v3/apps/available-endpoints/
|
||||
/// </remarks>
|
||||
/// <param name="installationId">The Id of the GitHub App Installation</param>
|
||||
IObservable<AccessToken> CreateInstallationToken(long installationId);
|
||||
}
|
||||
}
|
||||
85
Octokit.Reactive/Clients/ObservableGitHubAppsClient.cs
Normal file
85
Octokit.Reactive/Clients/ObservableGitHubAppsClient.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using Octokit.Reactive.Internal;
|
||||
using System;
|
||||
using System.Reactive.Threading.Tasks;
|
||||
|
||||
namespace Octokit.Reactive
|
||||
{
|
||||
/// <summary>
|
||||
/// A client for GitHub Applications API. Provides the methods required to get GitHub applications and installations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See the <a href="http://developer.github.com/v3/apps/">GitHub Apps API documentation</a> for more information.
|
||||
/// </remarks>
|
||||
public class ObservableGitHubAppsClient : IObservableGitHubAppsClient
|
||||
{
|
||||
private IGitHubAppsClient _client;
|
||||
private readonly IConnection _connection;
|
||||
|
||||
public ObservableGitHubAppsClient(IGitHubClient client)
|
||||
{
|
||||
Ensure.ArgumentNotNull(client, "client");
|
||||
|
||||
_client = client.GitHubApps;
|
||||
_connection = client.Connection;
|
||||
}
|
||||
|
||||
public IObservable<GitHubApp> Get(string slug)
|
||||
{
|
||||
return _client.Get(slug).ToObservable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the GitHub App associated with the authentication credentials used (requires GitHubApp JWT token auth).
|
||||
/// </summary>
|
||||
/// <remarks>https://developer.github.com/v3/apps/#get-the-authenticated-github-app</remarks>
|
||||
public IObservable<GitHubApp> GetCurrent()
|
||||
{
|
||||
return _client.GetCurrent().ToObservable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List installations of the authenticated GitHub App (requires GitHubApp JWT token auth).
|
||||
/// </summary>
|
||||
/// <remarks>https://developer.github.com/v3/apps/#find-installations</remarks>
|
||||
public IObservable<Installation> GetAllInstallationsForCurrent()
|
||||
{
|
||||
return _connection.GetAndFlattenAllPages<Installation>(ApiUrls.Installations(), null, AcceptHeaders.GitHubAppsPreview);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List installations of the authenticated GitHub App (requires GitHubApp JWT token auth).
|
||||
/// </summary>
|
||||
/// <param name="options">Options for changing the API response</param>
|
||||
/// <remarks>https://developer.github.com/v3/apps/#find-installations</remarks>
|
||||
public IObservable<Installation> GetAllInstallationsForCurrent(ApiOptions options)
|
||||
{
|
||||
Ensure.ArgumentNotNull(options, nameof(options));
|
||||
|
||||
return _connection.GetAndFlattenAllPages<Installation>(ApiUrls.Installations(), null, AcceptHeaders.GitHubAppsPreview, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a single GitHub App Installation (requires GitHubApp JWT token auth).
|
||||
/// </summary>
|
||||
/// <remarks>https://developer.github.com/v3/apps/#get-a-single-installation</remarks>
|
||||
/// <param name="installationId">The Id of the GitHub App Installation</param>
|
||||
public IObservable<Installation> GetInstallation(long installationId)
|
||||
{
|
||||
return _client.GetInstallation(installationId).ToObservable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a time bound access token for a GitHubApp Installation that can be used to access other API endpoints (requires GitHubApp JWT token auth).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// https://developer.github.com/v3/apps/#create-a-new-installation-token
|
||||
/// https://developer.github.com/apps/building-github-apps/authentication-options-for-github-apps/#authenticating-as-an-installation
|
||||
/// https://developer.github.com/v3/apps/available-endpoints/
|
||||
/// </remarks>
|
||||
/// <param name="installationId">The Id of the GitHub App Installation</param>
|
||||
public IObservable<AccessToken> CreateInstallationToken(long installationId)
|
||||
{
|
||||
return _client.CreateInstallationToken(installationId).ToObservable();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ namespace Octokit.Reactive
|
||||
|
||||
IObservableAuthorizationsClient Authorization { get; }
|
||||
IObservableActivitiesClient Activity { get; }
|
||||
IObservableGitHubAppsClient GitHubApps { get; }
|
||||
IObservableIssuesClient Issue { get; }
|
||||
IObservableMiscellaneousClient Miscellaneous { get; }
|
||||
IObservableOauthClient Oauth { get; }
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace Octokit.Reactive
|
||||
_gitHubClient = gitHubClient;
|
||||
Authorization = new ObservableAuthorizationsClient(gitHubClient);
|
||||
Activity = new ObservableActivitiesClient(gitHubClient);
|
||||
GitHubApps = new ObservableGitHubAppsClient(gitHubClient);
|
||||
Issue = new ObservableIssuesClient(gitHubClient);
|
||||
Miscellaneous = new ObservableMiscellaneousClient(gitHubClient);
|
||||
Oauth = new ObservableOauthClient(gitHubClient);
|
||||
@@ -69,6 +70,7 @@ namespace Octokit.Reactive
|
||||
|
||||
public IObservableAuthorizationsClient Authorization { get; private set; }
|
||||
public IObservableActivitiesClient Activity { get; private set; }
|
||||
public IObservableGitHubAppsClient GitHubApps { get; private set; }
|
||||
public IObservableIssuesClient Issue { get; private set; }
|
||||
public IObservableMiscellaneousClient Miscellaneous { get; private set; }
|
||||
public IObservableOauthClient Oauth { get; private set; }
|
||||
|
||||
@@ -33,6 +33,9 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Reactive" Version="3.1.0" />
|
||||
<PackageReference Include="SourceLink.Create.GitHub" Version="2.8.0" />
|
||||
<DotNetCliToolReference Include="dotnet-sourcelink-git" Version="2.8.0" />
|
||||
<DotNetCliToolReference Include="dotnet-sourcelink" Version="2.8.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
|
||||
@@ -40,10 +43,4 @@
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SourceLink.Create.GitHub" Version="2.1.0" PrivateAssets="All" />
|
||||
<DotNetCliToolReference Include="dotnet-sourcelink-git" Version="2.1.0" />
|
||||
<DotNetCliToolReference Include="dotnet-sourcelink" Version="2.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Octokit.Tests.Conventions
|
||||
|
||||
static string CreateMessage(Type modelType, IEnumerable<PropertyInfo> propertiesWithInvalidType)
|
||||
{
|
||||
return string.Format("Model type '{0}' contains the following properties that are named or suffixed with 'Url' but are not of type String: {!}{2}",
|
||||
return string.Format("Model type '{0}' contains the following properties that are named or suffixed with 'Url' but are not of type String: {1}{2}",
|
||||
modelType.FullName,
|
||||
Environment.NewLine,
|
||||
string.Join(Environment.NewLine, propertiesWithInvalidType.Select(x => x.Name)));
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
<AssemblyName>Octokit.Tests.Conventions</AssemblyName>
|
||||
<PackageId>Octokit.Tests.Conventions</PackageId>
|
||||
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
|
||||
<NetStandardImplicitPackageVersion>1.6.0</NetStandardImplicitPackageVersion>
|
||||
<RuntimeFrameworkVersion>1.0.4</RuntimeFrameworkVersion>
|
||||
<GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
|
||||
<GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
|
||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||
|
||||
130
Octokit.Tests.Integration/Clients/GitHubAppsClientTests.cs
Normal file
130
Octokit.Tests.Integration/Clients/GitHubAppsClientTests.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
using Octokit.Tests.Integration.Helpers;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Octokit.Tests.Integration.Clients
|
||||
{
|
||||
public class GitHubAppsClientTests
|
||||
{
|
||||
public class TheGetMethod
|
||||
{
|
||||
IGitHubClient _github;
|
||||
|
||||
public TheGetMethod()
|
||||
{
|
||||
// Regular authentication
|
||||
_github = Helper.GetAuthenticatedClient();
|
||||
}
|
||||
|
||||
[GitHubAppsTest]
|
||||
public async Task GetsApp()
|
||||
{
|
||||
var result = await _github.GitHubApps.Get(Helper.GitHubAppSlug);
|
||||
|
||||
Assert.Equal(Helper.GitHubAppId, result.Id);
|
||||
Assert.False(string.IsNullOrEmpty(result.Name));
|
||||
Assert.NotNull(result.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
public class TheGetCurrentMethod
|
||||
{
|
||||
IGitHubClient _github;
|
||||
|
||||
public TheGetCurrentMethod()
|
||||
{
|
||||
// Authenticate as a GitHubApp
|
||||
_github = Helper.GetAuthenticatedGitHubAppsClient();
|
||||
}
|
||||
|
||||
[GitHubAppsTest]
|
||||
public async Task GetsCurrentApp()
|
||||
{
|
||||
var result = await _github.GitHubApps.GetCurrent();
|
||||
|
||||
Assert.Equal(Helper.GitHubAppId, result.Id);
|
||||
Assert.False(string.IsNullOrEmpty(result.Name));
|
||||
Assert.NotNull(result.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
public class TheGetAllInstallationsForCurrentMethod
|
||||
{
|
||||
IGitHubClient _github;
|
||||
|
||||
public TheGetAllInstallationsForCurrentMethod()
|
||||
{
|
||||
// Authenticate as a GitHubApp
|
||||
_github = Helper.GetAuthenticatedGitHubAppsClient();
|
||||
}
|
||||
|
||||
[GitHubAppsTest]
|
||||
public async Task GetsAllInstallations()
|
||||
{
|
||||
var result = await _github.GitHubApps.GetAllInstallationsForCurrent();
|
||||
|
||||
Assert.True(result.Any(x => x.Id == Helper.GitHubAppInstallationId));
|
||||
foreach (var installation in result)
|
||||
{
|
||||
Assert.Equal(Helper.GitHubAppId, installation.AppId);
|
||||
Assert.NotNull(installation.Account);
|
||||
Assert.NotNull(installation.Permissions);
|
||||
Assert.Equal(InstallationPermissionLevel.Read, installation.Permissions.Metadata);
|
||||
Assert.False(string.IsNullOrEmpty(installation.HtmlUrl));
|
||||
Assert.NotEqual(0, installation.TargetId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TheGetInstallationMethod
|
||||
{
|
||||
IGitHubClient _github;
|
||||
|
||||
public TheGetInstallationMethod()
|
||||
{
|
||||
// Authenticate as a GitHubApp
|
||||
_github = Helper.GetAuthenticatedGitHubAppsClient();
|
||||
}
|
||||
|
||||
[GitHubAppsTest]
|
||||
public async Task GetsInstallation()
|
||||
{
|
||||
var installationId = Helper.GitHubAppInstallationId;
|
||||
|
||||
var result = await _github.GitHubApps.GetInstallation(installationId);
|
||||
|
||||
Assert.True(result.AppId == Helper.GitHubAppId);
|
||||
Assert.Equal(Helper.GitHubAppId, result.AppId);
|
||||
Assert.NotNull(result.Account);
|
||||
Assert.NotNull(result.Permissions);
|
||||
Assert.Equal(InstallationPermissionLevel.Read, result.Permissions.Metadata);
|
||||
Assert.False(string.IsNullOrEmpty(result.HtmlUrl));
|
||||
Assert.NotEqual(0, result.TargetId);
|
||||
}
|
||||
}
|
||||
|
||||
public class TheCreateInstallationTokenMethod
|
||||
{
|
||||
IGitHubClient _github;
|
||||
|
||||
public TheCreateInstallationTokenMethod()
|
||||
{
|
||||
// Authenticate as a GitHubApp
|
||||
_github = Helper.GetAuthenticatedGitHubAppsClient();
|
||||
}
|
||||
|
||||
[GitHubAppsTest]
|
||||
public async Task GetsInstallation()
|
||||
{
|
||||
var installationId = Helper.GitHubAppInstallationId;
|
||||
|
||||
var result = await _github.GitHubApps.CreateInstallationToken(installationId);
|
||||
|
||||
Assert.NotNull(result.Token);
|
||||
Assert.True(DateTimeOffset.Now < result.ExpiresAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,6 +64,34 @@ namespace Octokit.Tests.Integration
|
||||
return new Credentials(githubUsername, githubPassword);
|
||||
});
|
||||
|
||||
static readonly Lazy<bool> _gitHubAppsEnabled = new Lazy<bool>(() =>
|
||||
{
|
||||
string enabled = Environment.GetEnvironmentVariable("OCTOKIT_GITHUBAPP_ENABLED");
|
||||
return !String.IsNullOrWhiteSpace(enabled);
|
||||
});
|
||||
|
||||
static readonly Lazy<Credentials> _githubAppCredentials = new Lazy<Credentials>(() =>
|
||||
{
|
||||
// GitHubJwt nuget package only available for netstandard2.0+
|
||||
#if GITHUBJWT_HELPER_AVAILABLE
|
||||
var generator = new GitHubJwt.GitHubJwtFactory(
|
||||
new GitHubJwt.FilePrivateKeySource(GitHubAppPemFile),
|
||||
new GitHubJwt.GitHubJwtFactoryOptions
|
||||
{
|
||||
AppIntegrationId = Convert.ToInt32(GitHubAppId),
|
||||
ExpirationSeconds = 500
|
||||
}
|
||||
);
|
||||
|
||||
var jwtToken = generator.CreateEncodedJwtToken();
|
||||
return new Credentials(jwtToken, AuthenticationType.Bearer);
|
||||
#else
|
||||
// return null, which will cause the [GitHubAppTest]'s to not be discovered
|
||||
return null;
|
||||
#endif
|
||||
|
||||
});
|
||||
|
||||
static readonly Lazy<Uri> _customUrl = new Lazy<Uri>(() =>
|
||||
{
|
||||
string uri = Environment.GetEnvironmentVariable("OCTOKIT_CUSTOMURL");
|
||||
@@ -96,6 +124,8 @@ namespace Octokit.Tests.Integration
|
||||
|
||||
public static Credentials BasicAuthCredentials { get { return _basicAuthCredentials.Value; } }
|
||||
|
||||
public static Credentials GitHubAppCredentials { get { return _githubAppCredentials.Value; } }
|
||||
|
||||
public static Uri CustomUrl { get { return _customUrl.Value; } }
|
||||
|
||||
public static Uri TargetUrl { get { return CustomUrl ?? GitHubClient.GitHubApiUrl; } }
|
||||
@@ -126,6 +156,26 @@ namespace Octokit.Tests.Integration
|
||||
get { return Environment.GetEnvironmentVariable("OCTOKIT_CLIENTSECRET"); }
|
||||
}
|
||||
|
||||
public static long GitHubAppId
|
||||
{
|
||||
get { return Convert.ToInt64(Environment.GetEnvironmentVariable("OCTOKIT_GITHUBAPP_ID")); }
|
||||
}
|
||||
|
||||
public static string GitHubAppPemFile
|
||||
{
|
||||
get { return Environment.GetEnvironmentVariable("OCTOKIT_GITHUBAPP_PEMFILE"); }
|
||||
}
|
||||
|
||||
public static string GitHubAppSlug
|
||||
{
|
||||
get { return Environment.GetEnvironmentVariable("OCTOKIT_GITHUBAPP_SLUG"); }
|
||||
}
|
||||
|
||||
public static long GitHubAppInstallationId
|
||||
{
|
||||
get { return Convert.ToInt64(Environment.GetEnvironmentVariable("OCTOKIT_GITHUBAPP_INSTALLATIONID")); }
|
||||
}
|
||||
|
||||
public static void DeleteRepo(IConnection connection, Repository repository)
|
||||
{
|
||||
if (repository != null)
|
||||
@@ -244,6 +294,16 @@ namespace Octokit.Tests.Integration
|
||||
};
|
||||
}
|
||||
|
||||
public static bool IsGitHubAppsEnabled { get { return _gitHubAppsEnabled.Value; } }
|
||||
|
||||
public static GitHubClient GetAuthenticatedGitHubAppsClient()
|
||||
{
|
||||
return new GitHubClient(new ProductHeaderValue("OctokitTests"), TargetUrl)
|
||||
{
|
||||
Credentials = GitHubAppCredentials
|
||||
};
|
||||
}
|
||||
|
||||
public static void DeleteInvitations(IConnection connection, List<string> invitees, int teamId)
|
||||
{
|
||||
try
|
||||
|
||||
46
Octokit.Tests.Integration/Helpers/GitHubAppsTestAttribute.cs
Normal file
46
Octokit.Tests.Integration/Helpers/GitHubAppsTestAttribute.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Octokit.Tests.Integration
|
||||
{
|
||||
public class GitHubAppsTestDiscoverer : IXunitTestCaseDiscoverer
|
||||
{
|
||||
readonly IMessageSink diagnosticMessageSink;
|
||||
|
||||
public GitHubAppsTestDiscoverer(IMessageSink diagnosticMessageSink)
|
||||
{
|
||||
this.diagnosticMessageSink = diagnosticMessageSink;
|
||||
}
|
||||
|
||||
public IEnumerable<IXunitTestCase> Discover(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo factAttribute)
|
||||
{
|
||||
if (!Helper.IsGitHubAppsEnabled)
|
||||
return Enumerable.Empty<IXunitTestCase>();
|
||||
|
||||
Credentials creds = null;
|
||||
try
|
||||
{
|
||||
// Make sure we can generate GitHub App credentials
|
||||
creds = Helper.GitHubAppCredentials;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
if (creds == null)
|
||||
{
|
||||
return Enumerable.Empty<IXunitTestCase>();
|
||||
}
|
||||
|
||||
return new[] { new XunitTestCase(diagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), testMethod) };
|
||||
}
|
||||
}
|
||||
|
||||
[XunitTestCaseDiscoverer("Octokit.Tests.Integration.GitHubAppsTestDiscoverer", "Octokit.Tests.Integration")]
|
||||
public class GitHubAppsTestAttribute : FactAttribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,6 @@
|
||||
<AssemblyName>Octokit.Tests.Integration</AssemblyName>
|
||||
<PackageId>Octokit.Tests.Integration</PackageId>
|
||||
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
|
||||
<NetStandardImplicitPackageVersion>1.6.0</NetStandardImplicitPackageVersion>
|
||||
<RuntimeFrameworkVersion>1.0.4</RuntimeFrameworkVersion>
|
||||
<GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
|
||||
<GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
|
||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||
@@ -21,6 +19,10 @@
|
||||
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">
|
||||
<DefineConstants>$(DefineConstants);GITHUBJWT_HELPER_AVAILABLE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Octokit.Tests\Helpers\AssertEx.cs" />
|
||||
<EmbeddedResource Include="fixtures\hello-world.txt;fixtures\hello-world.zip" Exclude="bin\**;obj\**;**\*.xproj;packages\**;@(EmbeddedResource)" />
|
||||
@@ -43,4 +45,10 @@
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp1.0'">
|
||||
<PackageReference Include="GitHubJwt">
|
||||
<Version>0.0.2</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
105
Octokit.Tests/Clients/GitHubAppsClientTests.cs
Normal file
105
Octokit.Tests/Clients/GitHubAppsClientTests.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Octokit.Tests.Clients
|
||||
{
|
||||
public class GitHubAppsClientTests
|
||||
{
|
||||
public class TheCtor
|
||||
{
|
||||
[Fact]
|
||||
public void EnsuresNonNullArguments()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>(() => new GitHubAppsClient(null));
|
||||
}
|
||||
}
|
||||
|
||||
public class TheGetCurrentMethod
|
||||
{
|
||||
[Fact]
|
||||
public void GetsFromCorrectUrl()
|
||||
{
|
||||
var connection = Substitute.For<IApiConnection>();
|
||||
var client = new GitHubAppsClient(connection);
|
||||
|
||||
client.GetCurrent();
|
||||
|
||||
connection.Received().Get<GitHubApp>(Arg.Is<Uri>(u => u.ToString() == "app"), null, "application/vnd.github.machine-man-preview+json");
|
||||
}
|
||||
}
|
||||
|
||||
public class TheGetAllInstallationsForCurrentMethod
|
||||
{
|
||||
[Fact]
|
||||
public async Task EnsuresNonNullArguments()
|
||||
{
|
||||
var connection = Substitute.For<IApiConnection>();
|
||||
var client = new GitHubAppsClient(connection);
|
||||
|
||||
Assert.ThrowsAsync<ArgumentNullException>(() => client.GetAllInstallationsForCurrent(null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RequestsCorrectUrl()
|
||||
{
|
||||
var connection = Substitute.For<IApiConnection>();
|
||||
var client = new GitHubAppsClient(connection);
|
||||
|
||||
client.GetAllInstallationsForCurrent();
|
||||
|
||||
connection.Received().GetAll<Installation>(Arg.Is<Uri>(u => u.ToString() == "app/installations"), null, "application/vnd.github.machine-man-preview+json");
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void RequestsTheCorrectUrlWithApiOptions()
|
||||
{
|
||||
var connection = Substitute.For<IApiConnection>();
|
||||
var client = new GitHubAppsClient(connection);
|
||||
|
||||
var options = new ApiOptions
|
||||
{
|
||||
PageSize = 1,
|
||||
PageCount = 1,
|
||||
StartPage = 1
|
||||
};
|
||||
|
||||
client.GetAllInstallationsForCurrent(options);
|
||||
|
||||
connection.Received().GetAll<Installation>(Arg.Is<Uri>(u => u.ToString() == "app/installations"), null, "application/vnd.github.machine-man-preview+json", options);
|
||||
}
|
||||
}
|
||||
|
||||
public class TheGetInstallationMethod
|
||||
{
|
||||
[Fact]
|
||||
public void GetsFromCorrectUrl()
|
||||
{
|
||||
var connection = Substitute.For<IApiConnection>();
|
||||
var client = new GitHubAppsClient(connection);
|
||||
|
||||
client.GetInstallation(123);
|
||||
|
||||
connection.Received().Get<Installation>(Arg.Is<Uri>(u => u.ToString() == "app/installations/123"), null, "application/vnd.github.machine-man-preview+json");
|
||||
}
|
||||
}
|
||||
|
||||
public class TheCreateInstallationTokenMethod
|
||||
{
|
||||
[Fact]
|
||||
public void PostsToCorrectUrl()
|
||||
{
|
||||
var connection = Substitute.For<IApiConnection>();
|
||||
var client = new GitHubAppsClient(connection);
|
||||
|
||||
int fakeInstallationId = 3141;
|
||||
|
||||
client.CreateInstallationToken(fakeInstallationId);
|
||||
|
||||
connection.Received().Post<AccessToken>(Arg.Is<Uri>(u => u.ToString() == "installations/3141/access_tokens"), string.Empty, "application/vnd.github.machine-man-preview+json");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,6 @@
|
||||
<AssemblyName>Octokit.Tests</AssemblyName>
|
||||
<PackageId>Octokit.Tests</PackageId>
|
||||
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
|
||||
<NetStandardImplicitPackageVersion>1.6.0</NetStandardImplicitPackageVersion>
|
||||
<RuntimeFrameworkVersion>1.0.4</RuntimeFrameworkVersion>
|
||||
<GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
|
||||
<GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
|
||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||
@@ -31,7 +29,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Octokit\Octokit.csproj" />
|
||||
<ProjectReference Include="..\Octokit.Reactive\Octokit.Reactive.csproj" />
|
||||
<PackageReference Include="NSubstitute" Version="2.0.2" />
|
||||
<PackageReference Include="NSubstitute" Version="3.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
/// <summary>
|
||||
/// Delegated access to a third party
|
||||
/// </summary>
|
||||
Oauth
|
||||
Oauth,
|
||||
/// <summary>
|
||||
/// Credential for GitHub App using signed JWT
|
||||
/// </summary>
|
||||
Bearer
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@ namespace Octokit.Internal
|
||||
{
|
||||
{ AuthenticationType.Anonymous, new AnonymousAuthenticator() },
|
||||
{ AuthenticationType.Basic, new BasicAuthenticator() },
|
||||
{ AuthenticationType.Oauth, new TokenAuthenticator() }
|
||||
{ AuthenticationType.Oauth, new TokenAuthenticator() },
|
||||
{ AuthenticationType.Bearer, new BearerTokenAuthenticator() }
|
||||
};
|
||||
|
||||
public Authenticator(ICredentialStore credentialStore)
|
||||
|
||||
23
Octokit/Authentication/BearerTokenAuthenticator.cs
Normal file
23
Octokit/Authentication/BearerTokenAuthenticator.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Octokit.Internal
|
||||
{
|
||||
class BearerTokenAuthenticator: IAuthenticationHandler
|
||||
{
|
||||
public void Authenticate(IRequest request, Credentials credentials)
|
||||
{
|
||||
Ensure.ArgumentNotNull(request, nameof(request));
|
||||
Ensure.ArgumentNotNull(credentials, nameof(credentials));
|
||||
Ensure.ArgumentNotNull(credentials.Password, nameof(credentials.Password));
|
||||
|
||||
if (credentials.Login != null)
|
||||
{
|
||||
throw new InvalidOperationException("The Login is not null for a token authentication request. You " +
|
||||
"probably did something wrong.");
|
||||
}
|
||||
|
||||
request.Headers["Authorization"] = string.Format(CultureInfo.InvariantCulture, "Bearer {0}", credentials.Password);
|
||||
}
|
||||
}
|
||||
}
|
||||
82
Octokit/Clients/GitHubAppsClient.cs
Normal file
82
Octokit/Clients/GitHubAppsClient.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Octokit
|
||||
{
|
||||
/// <summary>
|
||||
/// A client for GitHub Applications API. Provides the methods required to get GitHub applications and installations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See the <a href="http://developer.github.com/v3/apps/">GitHub Apps API documentation</a> for more information.
|
||||
/// </remarks>
|
||||
public class GitHubAppsClient : ApiClient, IGitHubAppsClient
|
||||
{
|
||||
public GitHubAppsClient(IApiConnection apiConnection) : base(apiConnection)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a single GitHub App.
|
||||
/// </summary>
|
||||
/// <remarks>https://developer.github.com/v3/apps/#get-a-single-github-app</remarks>
|
||||
/// <param name="slug">The URL-friendly name of your GitHub App. You can find this on the settings page for your GitHub App.</param>
|
||||
public Task<GitHubApp> Get(string slug)
|
||||
{
|
||||
return ApiConnection.Get<GitHubApp>(ApiUrls.App(slug), null, AcceptHeaders.GitHubAppsPreview);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the GitHub App associated with the authentication credentials used (requires GitHubApp JWT token auth).
|
||||
/// </summary>
|
||||
/// <remarks>https://developer.github.com/v3/apps/#get-the-authenticated-github-app</remarks>
|
||||
public Task<GitHubApp> GetCurrent()
|
||||
{
|
||||
return ApiConnection.Get<GitHubApp>(ApiUrls.App(), null, AcceptHeaders.GitHubAppsPreview);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List installations of the authenticated GitHub App (requires GitHubApp JWT token auth).
|
||||
/// </summary>
|
||||
/// <remarks>https://developer.github.com/v3/apps/#find-installations</remarks>
|
||||
public Task<IReadOnlyList<Installation>> GetAllInstallationsForCurrent()
|
||||
{
|
||||
return ApiConnection.GetAll<Installation>(ApiUrls.Installations(), null, AcceptHeaders.GitHubAppsPreview);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List installations of the authenticated GitHub App (requires GitHubApp JWT token auth).
|
||||
/// </summary>
|
||||
/// <param name="options">Options for changing the API response</param>
|
||||
/// <remarks>https://developer.github.com/v3/apps/#find-installations</remarks>
|
||||
public Task<IReadOnlyList<Installation>> GetAllInstallationsForCurrent(ApiOptions options)
|
||||
{
|
||||
Ensure.ArgumentNotNull(options, nameof(options));
|
||||
|
||||
return ApiConnection.GetAll<Installation>(ApiUrls.Installations(), null, AcceptHeaders.GitHubAppsPreview, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a single GitHub App Installation (requires GitHubApp JWT token auth).
|
||||
/// </summary>
|
||||
/// <remarks>https://developer.github.com/v3/apps/#get-a-single-installation</remarks>
|
||||
/// <param name="installationId">The Id of the GitHub App Installation</param>
|
||||
public Task<Installation> GetInstallation(long installationId)
|
||||
{
|
||||
return ApiConnection.Get<Installation>(ApiUrls.Installation(installationId), null, AcceptHeaders.GitHubAppsPreview);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a time bound access token for a GitHubApp Installation that can be used to access other API endpoints (requires GitHubApp JWT token auth).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// https://developer.github.com/v3/apps/#create-a-new-installation-token
|
||||
/// https://developer.github.com/apps/building-github-apps/authentication-options-for-github-apps/#authenticating-as-an-installation
|
||||
/// https://developer.github.com/v3/apps/available-endpoints/
|
||||
/// </remarks>
|
||||
/// <param name="installationId">The Id of the GitHub App Installation</param>
|
||||
public Task<AccessToken> CreateInstallationToken(long installationId)
|
||||
{
|
||||
return ApiConnection.Post<AccessToken>(ApiUrls.AccessTokens(installationId), string.Empty, AcceptHeaders.GitHubAppsPreview);
|
||||
}
|
||||
}
|
||||
}
|
||||
58
Octokit/Clients/IGitHubAppsClient.cs
Normal file
58
Octokit/Clients/IGitHubAppsClient.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Octokit
|
||||
{
|
||||
/// <summary>
|
||||
/// A client for GitHub Applications API. Provides the methods required to get GitHub applications and installations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See the <a href="http://developer.github.com/v3/apps/">GitHub Apps API documentation</a> for more information.
|
||||
/// </remarks>
|
||||
public interface IGitHubAppsClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a single GitHub App.
|
||||
/// </summary>
|
||||
/// <remarks>https://developer.github.com/v3/apps/#get-a-single-github-app</remarks>
|
||||
/// <param name="slug">The URL-friendly name of your GitHub App. You can find this on the settings page for your GitHub App.</param>
|
||||
Task<GitHubApp> Get(string slug);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the GitHub App associated with the authentication credentials used (requires GitHubApp JWT token auth).
|
||||
/// </summary>
|
||||
/// <remarks>https://developer.github.com/v3/apps/#get-the-authenticated-github-app</remarks>
|
||||
Task<GitHubApp> GetCurrent();
|
||||
|
||||
/// <summary>
|
||||
/// List installations of the authenticated GitHub App (requires GitHubApp JWT token auth).
|
||||
/// </summary>
|
||||
/// <remarks>https://developer.github.com/v3/apps/#find-installations</remarks>
|
||||
Task<IReadOnlyList<Installation>> GetAllInstallationsForCurrent();
|
||||
|
||||
/// <summary>
|
||||
/// List installations of the authenticated GitHub App (requires GitHubApp JWT token auth).
|
||||
/// </summary>
|
||||
/// <param name="options">Options for changing the API response</param>
|
||||
/// <remarks>https://developer.github.com/v3/apps/#find-installations</remarks>
|
||||
Task<IReadOnlyList<Installation>> GetAllInstallationsForCurrent(ApiOptions options);
|
||||
|
||||
/// <summary>
|
||||
/// Get a single GitHub App Installation (requires GitHubApp JWT token auth).
|
||||
/// </summary>
|
||||
/// <remarks>https://developer.github.com/v3/apps/#get-a-single-installation</remarks>
|
||||
/// <param name="installationId">The Id of the GitHub App Installation</param>
|
||||
Task<Installation> GetInstallation(long installationId);
|
||||
|
||||
/// <summary>
|
||||
/// Create a time bound access token for a GitHubApp Installation that can be used to access other API endpoints (requires GitHubApp JWT token auth).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// https://developer.github.com/v3/apps/#create-a-new-installation-token
|
||||
/// https://developer.github.com/apps/building-github-apps/authentication-options-for-github-apps/#authenticating-as-an-installation
|
||||
/// https://developer.github.com/v3/apps/available-endpoints/
|
||||
/// </remarks>
|
||||
/// <param name="installationId">The Id of the GitHub App Installation</param>
|
||||
Task<AccessToken> CreateInstallationToken(long installationId);
|
||||
}
|
||||
}
|
||||
@@ -99,6 +99,7 @@ namespace Octokit
|
||||
Enterprise = new EnterpriseClient(apiConnection);
|
||||
Gist = new GistsClient(apiConnection);
|
||||
Git = new GitDatabaseClient(apiConnection);
|
||||
GitHubApps = new GitHubAppsClient(apiConnection);
|
||||
Issue = new IssuesClient(apiConnection);
|
||||
Migration = new MigrationClient(apiConnection);
|
||||
Miscellaneous = new MiscellaneousClient(connection);
|
||||
@@ -263,6 +264,14 @@ namespace Octokit
|
||||
/// </remarks>
|
||||
public IGitDatabaseClient Git { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Access GitHub's Apps API.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Refer to the API documentation for more information: https://developer.github.com/v3/apps/
|
||||
/// </remarks>
|
||||
public IGitHubAppsClient GitHubApps { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Access GitHub's Search API.
|
||||
/// </summary>
|
||||
|
||||
@@ -55,6 +55,8 @@ namespace Octokit
|
||||
|
||||
public const string NestedTeamsPreview = "application/vnd.github.hellcat-preview+json";
|
||||
|
||||
public const string GitHubAppsPreview = "application/vnd.github.machine-man-preview+json";
|
||||
|
||||
/// <summary>
|
||||
/// Combines multiple preview headers. GitHub API supports Accept header with multiple
|
||||
/// values separated by comma.
|
||||
|
||||
@@ -285,12 +285,54 @@ namespace Octokit
|
||||
/// Returns the <see cref="Uri"/> for the specified notification's subscription status.
|
||||
/// </summary>
|
||||
/// <param name="id">The Id of the notification.</param>
|
||||
/// <returns></returns>
|
||||
public static Uri NotificationSubscription(int id)
|
||||
{
|
||||
return "notifications/threads/{0}/subscription".FormatUri(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="Uri"/> for creating a new installation token.
|
||||
/// </summary>
|
||||
/// <param name="installationId">The Id of the GitHub App installation.</param>
|
||||
public static Uri AccessTokens(long installationId)
|
||||
{
|
||||
return "installations/{0}/access_tokens".FormatUri(installationId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="Uri"/> that creates a github app.
|
||||
/// </summary>
|
||||
public static Uri App()
|
||||
{
|
||||
return "app".FormatUri();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="Uri"/> that creates a github app.
|
||||
/// </summary>
|
||||
public static Uri App(string slug)
|
||||
{
|
||||
return "apps/{0}".FormatUri(slug);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="Uri"/> that returns all the installations of the authenticated application.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Uri Installations()
|
||||
{
|
||||
return "app/installations".FormatUri();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="Uri"/> that returns a single installation of the authenticated application.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Uri Installation(long installationId)
|
||||
{
|
||||
return "app/installations/{0}".FormatUri(installationId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="Uri"/> that returns all of the issues across all the authenticated user’s visible
|
||||
/// repositories including owned repositories, member repositories, and organization repositories:
|
||||
|
||||
@@ -148,7 +148,7 @@ namespace Octokit
|
||||
{
|
||||
throw new ArgumentException(
|
||||
string.Format(CultureInfo.InvariantCulture, "The base address '{0}' must be an absolute URI",
|
||||
baseAddress), "baseAddress");
|
||||
baseAddress), nameof(baseAddress));
|
||||
}
|
||||
|
||||
UserAgent = FormatUserAgent(productInformation);
|
||||
@@ -264,6 +264,14 @@ namespace Octokit
|
||||
return SendData<T>(uri, HttpMethod.Post, body, accepts, contentType, CancellationToken.None);
|
||||
}
|
||||
|
||||
public Task<IApiResponse<T>> Post<T>(Uri uri, object body, string accepts, string contentType, IDictionary<string, string> parameters)
|
||||
{
|
||||
Ensure.ArgumentNotNull(uri, nameof(uri));
|
||||
Ensure.ArgumentNotNull(body, nameof(body));
|
||||
|
||||
return SendData<T>(uri.ApplyParameters(parameters), 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"/>
|
||||
|
||||
@@ -13,23 +13,27 @@ namespace Octokit
|
||||
AuthenticationType = AuthenticationType.Anonymous;
|
||||
}
|
||||
|
||||
public Credentials(string token)
|
||||
{
|
||||
Ensure.ArgumentNotNullOrEmptyString(token, nameof(token));
|
||||
public Credentials(string login, string password) : this(login, password, AuthenticationType.Basic) { }
|
||||
|
||||
Login = null;
|
||||
Password = token;
|
||||
AuthenticationType = AuthenticationType.Oauth;
|
||||
}
|
||||
|
||||
public Credentials(string login, string password)
|
||||
public Credentials(string login, string password, AuthenticationType authenticationType)
|
||||
{
|
||||
Ensure.ArgumentNotNullOrEmptyString(login, nameof(login));
|
||||
Ensure.ArgumentNotNullOrEmptyString(password, nameof(password));
|
||||
|
||||
Login = login;
|
||||
Password = password;
|
||||
AuthenticationType = AuthenticationType.Basic;
|
||||
AuthenticationType = authenticationType;
|
||||
}
|
||||
|
||||
public Credentials(string token) : this(token, AuthenticationType.Oauth) { }
|
||||
|
||||
public Credentials(string token, AuthenticationType authenticationType)
|
||||
{
|
||||
Ensure.ArgumentNotNullOrEmptyString(token, nameof(token));
|
||||
|
||||
Login = null;
|
||||
Password = token;
|
||||
AuthenticationType = authenticationType;
|
||||
}
|
||||
|
||||
public string Login
|
||||
|
||||
@@ -126,8 +126,9 @@ namespace Octokit
|
||||
/// <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="parameters">Extra parameters for authentication.</param>
|
||||
/// <returns><seealso cref="IResponse"/> representing the received HTTP response</returns>
|
||||
Task<IApiResponse<T>> Post<T>(Uri uri, object body, string accepts, string contentType);
|
||||
Task<IApiResponse<T>> Post<T>(Uri uri, object body, string accepts, string contentType, IDictionary<string, string> parameters = null);
|
||||
|
||||
/// <summary>
|
||||
/// Performs an asynchronous HTTP POST request.
|
||||
|
||||
@@ -38,6 +38,14 @@ namespace Octokit
|
||||
/// </remarks>
|
||||
IActivitiesClient Activity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Access GitHub's Application API.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Refer to the API documentation for more information: https://developer.github.com/v3/apps/
|
||||
/// </remarks>
|
||||
IGitHubAppsClient GitHubApps { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Access GitHub's Issue API.
|
||||
/// </summary>
|
||||
|
||||
36
Octokit/Models/Response/AccessToken.cs
Normal file
36
Octokit/Models/Response/AccessToken.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Octokit
|
||||
{
|
||||
[DebuggerDisplay("{DebuggerDisplay,nq}")]
|
||||
public class AccessToken
|
||||
{
|
||||
public AccessToken() { }
|
||||
|
||||
public AccessToken(string token, DateTimeOffset expiresAt)
|
||||
{
|
||||
Token = token;
|
||||
ExpiresAt = expiresAt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The access token
|
||||
/// </summary>
|
||||
public string Token { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The expiration date
|
||||
/// </summary>
|
||||
public DateTimeOffset ExpiresAt { get; protected set; }
|
||||
|
||||
internal string DebuggerDisplay
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "Token: {0}, ExpiresAt: {1}", Token, ExpiresAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ namespace Octokit
|
||||
{
|
||||
public Repository Repository { get; protected set; }
|
||||
public User Sender { get; protected set; }
|
||||
public InstallationId Installation { get; protected set; }
|
||||
|
||||
internal string DebuggerDisplay
|
||||
{
|
||||
|
||||
72
Octokit/Models/Response/GitHubApp.cs
Normal file
72
Octokit/Models/Response/GitHubApp.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Octokit
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a GitHub application.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{DebuggerDisplay,nq}")]
|
||||
public class GitHubApp
|
||||
{
|
||||
public GitHubApp() { }
|
||||
|
||||
public GitHubApp(long id, string name, User owner, string description, string externalUrl, string htmlUrl, DateTimeOffset createdAt, DateTimeOffset updatedAt)
|
||||
{
|
||||
Id = id;
|
||||
Name = name;
|
||||
Owner = owner;
|
||||
Description = description;
|
||||
ExternalUrl = externalUrl;
|
||||
HtmlUrl = htmlUrl;
|
||||
CreatedAt = createdAt;
|
||||
UpdatedAt = updatedAt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Id of the GitHub App.
|
||||
/// </summary>
|
||||
public long Id { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Name of the GitHub App.
|
||||
/// </summary>
|
||||
public string Name { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Owner of the GitHub App.
|
||||
/// </summary>
|
||||
public User Owner { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Description of the GitHub App.
|
||||
/// </summary>
|
||||
public string Description { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The URL to the GitHub App's external website.
|
||||
/// </summary>
|
||||
public string ExternalUrl { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The URL to view the GitHub App on GitHub
|
||||
/// </summary>
|
||||
public string HtmlUrl { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Date the GitHub App was created.
|
||||
/// </summary>
|
||||
public DateTimeOffset CreatedAt { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Date the GitHub App was last updated.
|
||||
/// </summary>
|
||||
public DateTimeOffset UpdatedAt { get; protected set; }
|
||||
|
||||
internal string DebuggerDisplay
|
||||
{
|
||||
get { return string.Format(CultureInfo.InvariantCulture, "Id: {0} Name: {1}", Id, Name); }
|
||||
}
|
||||
}
|
||||
}
|
||||
91
Octokit/Models/Response/Installation.cs
Normal file
91
Octokit/Models/Response/Installation.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using Octokit.Internal;
|
||||
|
||||
namespace Octokit
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an application installation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// For more information see https://developer.github.com/v3/apps/#find-installations
|
||||
/// </remarks>
|
||||
[DebuggerDisplay("{DebuggerDisplay,nq}")]
|
||||
public class Installation : InstallationId
|
||||
{
|
||||
public Installation() { }
|
||||
|
||||
public Installation(long id, User account, string accessTokenUrl, string repositoriesUrl, string htmlUrl, long appId, long targetId, AccountType targetType, InstallationPermissions permissions, IReadOnlyList<string> events, string singleFileName, string repositorySelection) : base(id)
|
||||
{
|
||||
Account = account;
|
||||
HtmlUrl = htmlUrl;
|
||||
AppId = appId;
|
||||
TargetId = targetId;
|
||||
TargetType = targetType;
|
||||
Permissions = permissions;
|
||||
Events = events;
|
||||
SingleFileName = singleFileName;
|
||||
RepositorySelection = repositorySelection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The user who owns the Installation.
|
||||
/// </summary>
|
||||
public User Account { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The URL to view the Installation on GitHub.
|
||||
/// </summary>
|
||||
public string HtmlUrl { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Id of the associated GitHub App.
|
||||
/// </summary>
|
||||
public long AppId { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Id of the User/Organization the Installation is installed in
|
||||
/// </summary>
|
||||
public long TargetId { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of the target (User or Organization)
|
||||
/// </summary>
|
||||
public StringEnum<AccountType> TargetType { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Permissions granted to the Installation
|
||||
/// </summary>
|
||||
public InstallationPermissions Permissions { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Events subscribed to by the Installation
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> Events { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The single file the GitHub App can managem (when Permissions.SingleFile is set to read or write)
|
||||
/// </summary>
|
||||
public string SingleFileName { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The choice of repositories the installation is on. Can be either "selected" or "all".
|
||||
/// </summary>
|
||||
public StringEnum<InstallationRepositorySelection> RepositorySelection { get; protected set; }
|
||||
|
||||
internal new string DebuggerDisplay
|
||||
{
|
||||
get { return string.Format(CultureInfo.InvariantCulture, "Id: {0} AppId: {1}", Id, AppId); }
|
||||
}
|
||||
}
|
||||
|
||||
public enum InstallationRepositorySelection
|
||||
{
|
||||
[Parameter(Value = "all")]
|
||||
All,
|
||||
|
||||
[Parameter(Value = "selected")]
|
||||
Selected
|
||||
}
|
||||
}
|
||||
26
Octokit/Models/Response/InstallationId.cs
Normal file
26
Octokit/Models/Response/InstallationId.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Octokit
|
||||
{
|
||||
[DebuggerDisplay("{DebuggerDisplay,nq}")]
|
||||
public class InstallationId
|
||||
{
|
||||
public InstallationId() { }
|
||||
|
||||
public InstallationId(long id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Installation Id.
|
||||
/// </summary>
|
||||
public long Id { get; protected set; }
|
||||
|
||||
internal string DebuggerDisplay
|
||||
{
|
||||
get { return string.Format(CultureInfo.InvariantCulture, "Id: {0}", Id); }
|
||||
}
|
||||
}
|
||||
}
|
||||
121
Octokit/Models/Response/InstallationPermissions.cs
Normal file
121
Octokit/Models/Response/InstallationPermissions.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using Octokit.Internal;
|
||||
|
||||
namespace Octokit
|
||||
{
|
||||
[DebuggerDisplay("{DebuggerDisplay,nq}")]
|
||||
public class InstallationPermissions
|
||||
{
|
||||
public InstallationPermissions() { }
|
||||
|
||||
public InstallationPermissions(InstallationPermissionLevel? metadata, InstallationPermissionLevel? administration, InstallationPermissionLevel? statuses, InstallationPermissionLevel? deployments, InstallationPermissionLevel? issues, InstallationPermissionLevel? pages, InstallationPermissionLevel? pullRequests, InstallationPermissionLevel? contents, InstallationPermissionLevel? singleFile, InstallationPermissionLevel? repositoryProjects, InstallationPermissionLevel? members, InstallationPermissionLevel? organizationProjects, InstallationPermissionLevel? teamDiscussions)
|
||||
{
|
||||
Metadata = metadata;
|
||||
Administration = administration;
|
||||
Statuses = statuses;
|
||||
Deployments = deployments;
|
||||
Issues = issues;
|
||||
Pages = pages;
|
||||
PullRequests = pullRequests;
|
||||
Contents = contents;
|
||||
SingleFile = singleFile;
|
||||
RepositoryProjects = repositoryProjects;
|
||||
Members = members;
|
||||
OrganizationProjects = organizationProjects;
|
||||
TeamDiscussions = teamDiscussions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Repository metadata
|
||||
/// Search repositories, list collaborators, and access repository metadata.
|
||||
/// </summary>
|
||||
public StringEnum<InstallationPermissionLevel>? Metadata { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Repository administration
|
||||
/// Repository creation, deletion, settings, teams, and collaborators.
|
||||
/// </summary>
|
||||
public StringEnum<InstallationPermissionLevel>? Administration { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Commit statuses
|
||||
/// Commit statuses.
|
||||
/// </summary>
|
||||
public StringEnum<InstallationPermissionLevel>? Statuses { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Deployments
|
||||
/// Deployments and deployment statuses.
|
||||
/// </summary>
|
||||
public StringEnum<InstallationPermissionLevel>? Deployments { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Issues
|
||||
/// Issues and related comments, assignees, labels, and milestones.
|
||||
/// </summary>
|
||||
public StringEnum<InstallationPermissionLevel>? Issues { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Pages
|
||||
/// Retrieve Pages statuses, configuration, and builds, as well as create new builds.
|
||||
/// </summary>
|
||||
public StringEnum<InstallationPermissionLevel>? Pages { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Pull requests
|
||||
/// Pull requests and related comments, assignees, labels, milestones, and merges.
|
||||
/// </summary>
|
||||
public StringEnum<InstallationPermissionLevel>? PullRequests { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Repository contents
|
||||
/// Repository contents, commits, branches, downloads, releases, and merges.
|
||||
/// </summary>
|
||||
public StringEnum<InstallationPermissionLevel>? Contents { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Single file
|
||||
/// Manage just a single file.
|
||||
/// </summary>
|
||||
public StringEnum<InstallationPermissionLevel>? SingleFile { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Repository projects
|
||||
/// Manage repository projects, columns, and cards.
|
||||
/// </summary>
|
||||
public StringEnum<InstallationPermissionLevel>? RepositoryProjects { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Organization members (only applicable when installed for an Organization )
|
||||
/// Organization members and teams.
|
||||
/// </summary>
|
||||
public StringEnum<InstallationPermissionLevel>? Members { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Organization projects (only applicable when installed for an Organization )
|
||||
/// Manage organization projects, columns, and cards.
|
||||
/// </summary>
|
||||
public StringEnum<InstallationPermissionLevel>? OrganizationProjects { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Team discussions (only applicable when installed for an Organization )
|
||||
/// Team discussions.
|
||||
/// </summary>
|
||||
public StringEnum<InstallationPermissionLevel>? TeamDiscussions { get; protected set; }
|
||||
|
||||
internal string DebuggerDisplay
|
||||
{
|
||||
get { return string.Format(CultureInfo.InvariantCulture, "Metadata: {0}, Contents: {1}, Issues: {2}, Single File: {3}", Metadata, Contents, Issues, SingleFile); }
|
||||
}
|
||||
}
|
||||
|
||||
public enum InstallationPermissionLevel
|
||||
{
|
||||
[Parameter(Value = "read")]
|
||||
Read,
|
||||
|
||||
[Parameter(Value = "write")]
|
||||
Write
|
||||
}
|
||||
}
|
||||
@@ -37,9 +37,9 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SourceLink.Create.GitHub" Version="2.1.0" PrivateAssets="All" />
|
||||
<DotNetCliToolReference Include="dotnet-sourcelink-git" Version="2.1.0" />
|
||||
<DotNetCliToolReference Include="dotnet-sourcelink" Version="2.1.0" />
|
||||
<PackageReference Include="SourceLink.Create.GitHub" Version="2.8.0" />
|
||||
<DotNetCliToolReference Include="dotnet-sourcelink-git" Version="2.8.0" />
|
||||
<DotNetCliToolReference Include="dotnet-sourcelink" Version="2.8.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1293,6 +1293,7 @@ namespace Octokit
|
||||
private static readonly string[] Iso8601Format = new string[]
|
||||
{
|
||||
@"yyyy-MM-dd\THH:mm:ss.FFFFFFF\Z",
|
||||
@"yyyy-MM-dd\THH:mm:ss.FFFFFFFK",
|
||||
@"yyyy-MM-dd\THH:mm:ss\Z",
|
||||
@"yyyy-MM-dd\THH:mm:ssK"
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ init:
|
||||
|
||||
build_script:
|
||||
- dotnet --info
|
||||
- ps: .\build.ps1 -LinkSources
|
||||
- ps: .\build.ps1 -LinkSources -Verbosity Verbose
|
||||
|
||||
test: off
|
||||
|
||||
|
||||
10
build.ps1
10
build.ps1
@@ -26,14 +26,14 @@ Param(
|
||||
[string]$Configuration = "Release",
|
||||
[switch]$LinkSources,
|
||||
[ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
|
||||
[string]$Verbosity = "Verbose",
|
||||
[string]$Verbosity = "Normal",
|
||||
[switch]$WhatIf,
|
||||
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
|
||||
[string[]]$ScriptArgs
|
||||
)
|
||||
|
||||
$DotNetChannel = "preview";
|
||||
$DotNetVersion = "1.0.1";
|
||||
$DotNetVersion = "1.0.4";
|
||||
$DotNetInstallerUri = "https://raw.githubusercontent.com/dotnet/cli/rel/1.0.1/scripts/obtain/dotnet-install.ps1";
|
||||
$NugetUrl = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
|
||||
|
||||
@@ -106,14 +106,14 @@ $Arguments = @{
|
||||
# Start Cake
|
||||
Push-Location
|
||||
Set-Location build
|
||||
Write-Host "Restoring packages..."
|
||||
Write-Host "Preparing Cake.Frosting build runner..."
|
||||
Invoke-Expression "dotnet restore"
|
||||
if($LASTEXITCODE -ne 0) {
|
||||
Pop-Location;
|
||||
exit $LASTEXITCODE;
|
||||
}
|
||||
Write-Host "Running build..."
|
||||
Invoke-Expression "dotnet run -- $Arguments"
|
||||
Write-Host "Running Cake.Frosting build runner..."
|
||||
Invoke-Expression "dotnet run $Arguments"
|
||||
if($LASTEXITCODE -ne 0) {
|
||||
Pop-Location;
|
||||
exit $LASTEXITCODE;
|
||||
|
||||
5
build.sh
5
build.sh
@@ -7,5 +7,8 @@ if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
||||
fi
|
||||
|
||||
cd build
|
||||
echo "Preparing Cake.Frosting build runner..."
|
||||
dotnet restore
|
||||
dotnet run -- "$@"
|
||||
|
||||
echo "Executing Cake.Frosting build runner..."
|
||||
dotnet run "$@"
|
||||
15
build/.vscode/launch.json
vendored
15
build/.vscode/launch.json
vendored
@@ -1,4 +1,7 @@
|
||||
{
|
||||
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||
// Use hover for the description of the existing attributes
|
||||
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
@@ -6,10 +9,12 @@
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceRoot}\\bin\\Debug\\netcoreapp1.0\\build.dll",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/bin/Debug/netcoreapp2.0/Build.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"externalConsole": false,
|
||||
"cwd": "${workspaceFolder}",
|
||||
// For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false,
|
||||
"internalConsoleOptions": "openOnSessionStart"
|
||||
},
|
||||
@@ -17,7 +22,7 @@
|
||||
"name": ".NET Core Attach",
|
||||
"type": "coreclr",
|
||||
"request": "attach",
|
||||
"processId": "${command.pickProcess}"
|
||||
"processId": "${command:pickProcess}"
|
||||
}
|
||||
]
|
||||
,]
|
||||
}
|
||||
13
build/.vscode/tasks.json
vendored
13
build/.vscode/tasks.json
vendored
@@ -1,15 +1,14 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"command": "dotnet",
|
||||
"isShellCommand": true,
|
||||
"args": [],
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"taskName": "build",
|
||||
"label": "build",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"${workspaceRoot}\\project.json"
|
||||
"build",
|
||||
"${workspaceFolder}/Build.csproj"
|
||||
],
|
||||
"isBuildCommand": true,
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -13,7 +13,7 @@ public class Build : FrostingTask<Context>
|
||||
Configuration = context.Configuration,
|
||||
ArgumentCustomization = args => args
|
||||
.Append("/p:Version={0}", context.Version.GetSemanticVersion())
|
||||
.AppendIfTrue(context.LinkSources, "/p:SourceLinkCreate=true")
|
||||
.Append("/p:SourceLinkCreate={0}", context.LinkSources.ToString().ToLower())
|
||||
});
|
||||
}
|
||||
}
|
||||
131
docs/github-apps.md
Normal file
131
docs/github-apps.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# Working with GitHub Apps
|
||||
|
||||
## Overview
|
||||
GitHub Apps are a new type of integration offering narrow, specific permissions, compared to OAuth apps or user authentication.
|
||||
|
||||
To learn more, head to the GitHub Apps section under the GitHub Developer [Getting started with Building Apps](https://developer.github.com/apps/getting-started-with-building-apps/#using-github-apps) documentation.
|
||||
|
||||
A GitHub App (known in Octokit as a `GitHubApp`) specifies permissions (read, write, none) it will be granted for various scopes and also registers for various webhook events.
|
||||
|
||||
A GitHub App is installed in an `Organization` or `User` account (known in Octokit as an `Installation`) where it is further limited to nominated (or all) repositories for that account.
|
||||
|
||||
The [GitHub Api Documentation](https://developer.github.com/v3/apps/) on GitHub Apps contains more detailed information.
|
||||
|
||||
## Authentication
|
||||
|
||||
The below walkthrough outlines how to authenticate as a `GitHubApp` and an `Installation` using Octokit.net.
|
||||
|
||||
Be sure to see the [GitHub Api Documentation](https://developer.github.com/apps/building-github-apps/authentication-options-for-github-apps/#authentication-options-for-github-apps) on GitHub Apps authentication, if you require more details.
|
||||
|
||||
## GitHub App Walkthrough
|
||||
|
||||
Each GitHub App has a private certificate (PEM file) generated through the GitHub website and possessed by the owner of the GitHub App. Authentication occurs using a time based JWT token, signed by the GitHub App's private certificate.
|
||||
|
||||
``` csharp
|
||||
// A time based JWT token, signed by the GitHub App's private certificate
|
||||
var jwtToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MjA0Mjc3MTQsImV4cCI6MTUyMDQyODMxNCwiaXNzIjo5NzM1fQ.K-d3FKWKddMygFqvPZYWQusqhbF1LYfcIM0VbBq4uJsS9VkjhyXALlHmTJWjdblzx-U55lkZc_KWdJd6GlDxvoRb5w_9nrLcIFRbYVgi9XTYpCc3o5j7Qh3FvKxA1bzEs8XGrxjjE7-WJn_xi85ugFKTy9tlIRPa-PHeIOvNp4fz4ru8SFPoD4epiraeEyLfpU_ke-HYF7Ws7ar19zQkfJKRHSIFm1LxJ5MGKWT8pQBBUSGxGPgEG_tYI83aYw6cVx-DLV290bpr23LRUC684Wv_XabUDzXjPUYynAc01APZF6aN8B0LHdPbG8I6Yd74sQfmN-aHz5moz8ZNWLNm8Q";
|
||||
|
||||
// Use the JWT as a Bearer token
|
||||
var gitHubClient = new GitHubClient(new ProductHeaderValue("MyApp"))
|
||||
{
|
||||
Credentials = new Credentials(jwtToken, AuthenticationType.Bearer)
|
||||
};
|
||||
```
|
||||
|
||||
The authenticated `GitHubApp` can query various top level information about itself:
|
||||
|
||||
``` csharp
|
||||
// Get the current authenticated GitHubApp
|
||||
var app = await appClient.GetCurrent();
|
||||
|
||||
// Get a list of installations for the authenticated GitHubApp
|
||||
var installations = await appClient.GetAllInstallationsForCurrent();
|
||||
|
||||
// Get a specific installation of the authenticated GitHubApp by it's installation Id
|
||||
var installation = await appClient.GetInstallation(123);
|
||||
|
||||
```
|
||||
|
||||
In order to do more than top level calls, a `GitHubApp` needs to authenticate as a specific `Installation` by creating a temporary installation token, and using that for authentication:
|
||||
|
||||
``` csharp
|
||||
// Create an Installation token for Insallation Id 123
|
||||
var response = await appClient.CreateInstallationToken(123);
|
||||
|
||||
// NOTE - the token will expire!
|
||||
response.ExpiresAt;
|
||||
|
||||
// Create a new GitHubClient using the installation token as authentication
|
||||
var installationClient = new GitHubClient(new ProductHeaderValue("MyApp-Installation123"))
|
||||
{
|
||||
Credentials = new Credentials(response.Token)
|
||||
};
|
||||
```
|
||||
|
||||
Once authenticated as an `Installation`, a [subset of regular API endpoints](https://developer.github.com/v3/apps/available-endpoints/) can be queried, using the permissions of the `GitHubApp` and repository settings of the `Installation`:
|
||||
|
||||
``` csharp
|
||||
// Create a Comment on an Issue
|
||||
// - Assuming the GitHub App has read/write permission for issues scope
|
||||
// - Assuming we are operating on a repository that the Installation has access to
|
||||
var response = await installationClient.Issue.Comment.Create("owner", "repo", 1, "Hello from my GitHubApp Installation!");
|
||||
```
|
||||
|
||||
## A Note on identifying Installation Id's
|
||||
GitHub Apps can be registered for webhook events.
|
||||
|
||||
WebHook payloads sent to these registrations now include an extra field to indicate the Id of the GitHub App Installation that is associated with the received webhook.
|
||||
|
||||
Example webhook for an opened Pull Request:
|
||||
``` json
|
||||
{
|
||||
"action": "opened",
|
||||
"number": 1,
|
||||
"pull_request": {
|
||||
...
|
||||
},
|
||||
"repository": {
|
||||
...
|
||||
},
|
||||
"sender": {
|
||||
...
|
||||
},
|
||||
"installation": {
|
||||
"id": 1234
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can retrieve this `installation.id` from your webhook payload, and use it to create the Installation token as above, to further interact with the repository as that Installation of the GitHub App.
|
||||
|
||||
Although Octokit.net doesn't have explicit webhook support, the `ActivityPayload` response classes do generally align with webhook payloads (assuming the octokit.net custom deserializer is used), so we have added the field to these:
|
||||
|
||||
``` csharp
|
||||
// json payload from the received webhook
|
||||
var json = "...";
|
||||
|
||||
// Deserialize the pull_request event
|
||||
var serializer = new Octokit.Internal.SimpleJsonSerializer();
|
||||
var payload = serializer_.Deserialize<PullRequestEventPayload>(json);
|
||||
|
||||
// Create an Installation token for the associated Insallation Id
|
||||
var response = await appClient.CreateInstallationToken(payload.Installation.Id);
|
||||
```
|
||||
|
||||
## A Note on JWT Tokens
|
||||
Octokit.net expects that you will pass in the appropriately signed JWT token required to authenticate the `GitHubApp`.
|
||||
|
||||
Luckily one of our contributors [@adriangodong](https://github.com/adriangodong) has created a library `GitHubJwt` ( [GitHub](https://github.com/adriangodong/githubjwt) | [NuGet](https://www.nuget.org/packages/githubjwt) ) which you can use to help with this (as long as you are targetting `netstandard2.0` or above).
|
||||
|
||||
``` csharp
|
||||
var generator = new GitHubJwt.GitHubJwtFactory(
|
||||
new GitHubJwt.FilePrivateKeySource("/path/to/pem_file"),
|
||||
new GitHubJwt.GitHubJwtFactoryOptions
|
||||
{
|
||||
AppIntegrationId = 123, // The GitHub App Id
|
||||
ExpirationSeconds = 600 // 10 minutes is the maximum time allowed
|
||||
}
|
||||
);
|
||||
|
||||
var jwtToken = generator.CreateEncodedJwtToken();
|
||||
```
|
||||
@@ -24,12 +24,14 @@ pages:
|
||||
- 'Releases' : 'releases.md'
|
||||
- 'Search' : 'search.md'
|
||||
- 'Git Database' : 'git-database.md'
|
||||
- 'GitHub Apps' : 'github-apps.md'
|
||||
|
||||
- Samples:
|
||||
- 'Exploring Pull Requests' : 'demos/exploring-pull-requests.md'
|
||||
|
||||
- Advanced:
|
||||
- 'API Options': 'extensibility.md'
|
||||
- 'Working with Enums': 'working-with-enums.md'
|
||||
- 'Debugging from Source': 'debugging-source.md'
|
||||
- 'OAuth Flow': 'oauth-flow.md'
|
||||
- 'HttpClient': 'http-client.md'
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Builds and tests Octokit
|
||||
.DESCRIPTION
|
||||
Janky runs this script after checking out a revision and cleaning its
|
||||
working tree.
|
||||
.PARAMETER Clean
|
||||
When true, all untracked (and ignored) files will be removed from the work
|
||||
tree. Defaults to false.
|
||||
#>
|
||||
|
||||
Param(
|
||||
[switch]
|
||||
$Clean = $false
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
|
||||
try {
|
||||
# Make the output width reeeeeaaaaaly wide so our output doesn't get hard-wrapped.
|
||||
# <http://stackoverflow.com/questions/978777/powershell-output-column-width>
|
||||
$Host.UI.RawUI.BufferSize = New-Object Management.Automation.Host.Size -ArgumentList 5000, 25
|
||||
} catch {
|
||||
# Most likely we were run in a cmd.exe terminal which won't allow setting
|
||||
# the BufferSize to such a strange size.
|
||||
}
|
||||
|
||||
$rootDirectory = Split-Path (Split-Path $MyInvocation.MyCommand.Path)
|
||||
|
||||
Push-Location $rootDirectory
|
||||
|
||||
function Die-WithOutput($exitCode, $output) {
|
||||
Write-Output $output
|
||||
Write-Output ""
|
||||
exit $exitCode
|
||||
}
|
||||
|
||||
function Dump-Error($output) {
|
||||
$exitCode = $LastExitCode
|
||||
|
||||
$errors = $output | Select-String ": error"
|
||||
if ($errors) {
|
||||
$output = "Likely errors:", $errors, "", "Full output:", $output
|
||||
}
|
||||
|
||||
Die-WithOutput $exitCode $output
|
||||
}
|
||||
|
||||
function Run-Command([scriptblock]$Command, [switch]$Fatal, [switch]$Quiet) {
|
||||
$output = ""
|
||||
if ($Quiet) {
|
||||
$output = & $Command 2>&1
|
||||
} else {
|
||||
& $Command
|
||||
}
|
||||
|
||||
if (!$Fatal) {
|
||||
return
|
||||
}
|
||||
|
||||
$exitCode = 0
|
||||
if ($LastExitCode -ne 0) {
|
||||
$exitCode = $LastExitCode
|
||||
} elseif (!$?) {
|
||||
$exitCode = 1
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
$error = "Error executing command ``$Command``."
|
||||
if ($output) {
|
||||
$error = "$error Output:", $output
|
||||
}
|
||||
Die-WithOutput $exitCode $error
|
||||
}
|
||||
|
||||
if ($Clean) {
|
||||
Write-Output "Cleaning work tree..."
|
||||
Write-Output ""
|
||||
|
||||
Run-Command -Quiet -Fatal { git clean -xdf }
|
||||
}
|
||||
|
||||
|
||||
Write-Output "Installing FAKE..."
|
||||
Write-Output ""
|
||||
.\tools\nuget\nuget.exe "install" "FAKE.Core" "-OutputDirectory" "tools" "-ExcludeVersion" "-Version" "2.18.1"
|
||||
|
||||
Write-Output "Building Octokit..."
|
||||
Write-Output ""
|
||||
$output = & .\tools\FAKE.Core\tools\Fake.exe "build.fsx" "target=BuildApp" "buildMode=Release"
|
||||
if ($LastExitCode -ne 0) {
|
||||
Dump-Error($output)
|
||||
}
|
||||
|
||||
Write-Output "Running unit tests..."
|
||||
Write-Output ""
|
||||
$output = & .\tools\FAKE.Core\tools\Fake.exe "build.fsx" "target=UnitTests" "buildMode=Release"
|
||||
if ($LastExitCode -ne 0) {
|
||||
Dump-Error($output)
|
||||
}
|
||||
|
||||
|
||||
Write-Output "Running convention tests..."
|
||||
Write-Output ""
|
||||
$output = & .\tools\FAKE.Core\tools\Fake.exe "build.fsx" "target=ConventionTests" "buildMode=Release"
|
||||
if ($LastExitCode -ne 0) {
|
||||
Dump-Error($output)
|
||||
}
|
||||
|
||||
Write-Output "Running integration tests..."
|
||||
Write-Output ""
|
||||
$output = & .\tools\FAKE.Core\tools\Fake.exe "build.fsx" "target=IntegrationTests" "buildMode=Release"
|
||||
if ($LastExitCode -ne 0) {
|
||||
Dump-Error($output)
|
||||
}
|
||||
|
||||
Write-Output "Creating NuGet packages..."
|
||||
Write-Output ""
|
||||
$output = & .\tools\FAKE.Core\tools\Fake.exe "build.fsx" "target=CreatePackages" "buildMode=Release"
|
||||
if ($LastExitCode -ne 0) {
|
||||
Dump-Error($output)
|
||||
}
|
||||
|
||||
$exitCode = 0
|
||||
|
||||
exit $exitCode
|
||||
@@ -98,6 +98,13 @@ VerifyEnvironmentVariable "Override GitHub URL" "OCTOKIT_CUSTOMURL" $true
|
||||
VerifyEnvironmentVariable "application ClientID" "OCTOKIT_CLIENTID" $true
|
||||
VerifyEnvironmentVariable "application Secret" "OCTOKIT_CLIENTSECRET" $true
|
||||
|
||||
if (AskYesNoQuestion "Do you wish to setup GitHubApps integration test settings?" "OCTOKIT_GITHUBAPP_ENABLED")
|
||||
{
|
||||
VerifyEnvironmentVariable "GitHub App ID" "OCTOKIT_GITHUBAPP_ID"
|
||||
VerifyEnvironmentVariable "GitHub App SLUG" "OCTOKIT_GITHUBAPP_SLUG"
|
||||
VerifyEnvironmentVariable "GitHub App Installation ID" "OCTOKIT_GITHUBAPP_INSTALLATIONID"
|
||||
VerifyEnvironmentVariable "GitHub App Pem File" "OCTOKIT_GITHUBAPP_PEMFILE"
|
||||
}
|
||||
|
||||
if (AskYesNoQuestion "Do you wish to enable GitHub Enterprise (GHE) Integration Tests?" "OCTOKIT_GHE_ENABLED")
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user