diff --git a/Octokit.Reactive/Clients/IObservableGitHubAppInstallationsClient.cs b/Octokit.Reactive/Clients/IObservableGitHubAppInstallationsClient.cs new file mode 100644 index 00000000..58b173f7 --- /dev/null +++ b/Octokit.Reactive/Clients/IObservableGitHubAppInstallationsClient.cs @@ -0,0 +1,41 @@ +using System; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub Applications Installations API. + /// + /// + /// See the GitHub Apps Installations API documentation for more information. + /// + public interface IObservableGitHubAppInstallationsClient + { + /// + /// List repositories of the authenticated GitHub App Installation (requires GitHubApp Installation-Token auth). + /// + /// https://developer.github.com/v3/apps/installations/#list-repositories + IObservable GetAllRepositoriesForCurrent(); + + /// + /// List repositories of the authenticated GitHub App Installation (requires GitHubApp Installation-Token auth). + /// + /// Options for changing the API response + /// https://developer.github.com/v3/apps/installations/#list-repositories + IObservable GetAllRepositoriesForCurrent(ApiOptions options); + + /// + /// List repositories accessible to the user for an installation (requires GitHubApp User-To-Server Auth). + /// + /// The Id of the installation + /// https://developer.github.com/v3/apps/installations/#list-repositories-accessible-to-the-user-for-an-installation + IObservable GetAllRepositoriesForCurrentUser(long installationId); + + /// + /// List repositories accessible to the user for an installation (requires GitHubApp User-To-Server Auth). + /// + /// The Id of the installation + /// Options for changing the API response + /// https://developer.github.com/v3/apps/installations/#list-repositories-accessible-to-the-user-for-an-installation + IObservable GetAllRepositoriesForCurrentUser(long installationId, ApiOptions options); + } +} \ No newline at end of file diff --git a/Octokit.Reactive/Clients/IObservableGitHubAppsClient.cs b/Octokit.Reactive/Clients/IObservableGitHubAppsClient.cs index 65f4ba4a..30a7e70d 100644 --- a/Octokit.Reactive/Clients/IObservableGitHubAppsClient.cs +++ b/Octokit.Reactive/Clients/IObservableGitHubAppsClient.cs @@ -3,7 +3,7 @@ namespace Octokit.Reactive { /// - /// A client for GitHub Applications API. Provides the methods required to get GitHub applications and installations. + /// A client for GitHub Applications API. /// /// /// See the GitHub Apps API documentation for more information. @@ -11,40 +11,68 @@ namespace Octokit.Reactive public interface IObservableGitHubAppsClient { /// - /// Get a single GitHub App. + /// Access GitHub's Apps Installations API. + /// + /// + /// Refer to the API documentation for more information: https://developer.github.com/v3/apps/installations/ + /// + IObservableGitHubAppInstallationsClient Installation { get; } + + /// + /// Get a single GitHub App (if private, requires Personal Access Token or GitHubApp auth) /// /// https://developer.github.com/v3/apps/#get-a-single-github-app /// The URL-friendly name of your GitHub App. You can find this on the settings page for your GitHub App. IObservable Get(string slug); /// - /// Returns the GitHub App associated with the authentication credentials used (requires GitHubApp JWT token auth). + /// Returns the GitHub App associated with the authentication credentials used (requires GitHubApp auth). /// /// https://developer.github.com/v3/apps/#get-the-authenticated-github-app IObservable GetCurrent(); /// - /// List installations of the authenticated GitHub App (requires GitHubApp JWT token auth). + /// List installations of the authenticated GitHub App (requires GitHubApp auth). /// /// https://developer.github.com/v3/apps/#find-installations IObservable GetAllInstallationsForCurrent(); /// - /// List installations of the authenticated GitHub App (requires GitHubApp JWT token auth). + /// List installations of the authenticated GitHub App (requires GitHubApp auth). /// /// Options for changing the API response /// https://developer.github.com/v3/apps/#find-installations IObservable GetAllInstallationsForCurrent(ApiOptions options); /// - /// Get a single GitHub App Installation (requires GitHubApp JWT token auth). + /// Get a single GitHub App Installation (requires GitHubApp auth). /// /// https://developer.github.com/v3/apps/#get-a-single-installation /// The Id of the GitHub App Installation + [Obsolete("This method will be removed in a future release. Please use GetInstallationForCurrent() instead")] IObservable GetInstallation(long installationId); /// - /// Create a time bound access token for a GitHubApp Installation that can be used to access other API endpoints (requires GitHubApp JWT token auth). + /// Get a single GitHub App Installation (requires GitHubApp auth). + /// + /// https://developer.github.com/v3/apps/#get-a-single-installation + /// The Id of the GitHub App Installation + IObservable GetInstallationForCurrent(long installationId); + + /// + /// List installations for the currently authenticated user (requires GitHubApp User-To-Server Auth). + /// + /// https://developer.github.com/v3/apps/#list-installations-for-user + IObservable GetAllInstallationsForCurrentUser(); + + /// + /// List installations for the currently authenticated user (requires GitHubApp User-To-Server Auth). + /// + /// https://developer.github.com/v3/apps/#list-installations-for-user + IObservable GetAllInstallationsForCurrentUser(ApiOptions options); + + /// + /// Create a time bound access token for a GitHubApp Installation that can be used to access other API endpoints (requires GitHubApp auth). /// /// /// https://developer.github.com/v3/apps/#create-a-new-installation-token @@ -53,5 +81,34 @@ namespace Octokit.Reactive /// /// The Id of the GitHub App Installation IObservable CreateInstallationToken(long installationId); + + /// + /// Enables an authenticated GitHub App to find the organization's installation information (requires GitHubApp auth). + /// + /// https://developer.github.com/v3/apps/#find-organization-installation + /// The name of the organization + IObservable GetOrganizationInstallationForCurrent(string organization); + + /// + /// Enables an authenticated GitHub App to find the repository's installation information (requires GitHubApp auth). + /// + /// https://developer.github.com/v3/apps/#find-repository-installation + /// The owner of the repo + /// The name of the repo + IObservable GetRepositoryInstallationForCurrent(string owner, string repo); + + /// + /// Enables an authenticated GitHub App to find the repository's installation information (requires GitHubApp auth). + /// + /// https://developer.github.com/v3/apps/#find-repository-installation + /// The Id of the repository + IObservable GetRepositoryInstallationForCurrent(long repositoryId); + + /// + /// Enables an authenticated GitHub App to find the users's installation information (requires GitHubApp auth). + /// + /// https://developer.github.com/v3/apps/#find-user-installation + /// The name of the user + IObservable GetUserInstallationForCurrent(string user); } } diff --git a/Octokit.Reactive/Clients/ObservableGitHubAppInstallationsClient.cs b/Octokit.Reactive/Clients/ObservableGitHubAppInstallationsClient.cs new file mode 100644 index 00000000..df4a4a8a --- /dev/null +++ b/Octokit.Reactive/Clients/ObservableGitHubAppInstallationsClient.cs @@ -0,0 +1,65 @@ +using System; +using Octokit.Reactive.Internal; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub Applications Installations API. + /// + /// + /// See the GitHub Apps Installations API documentation for more information. + /// + public class ObservableGitHubAppInstallationsClient : IObservableGitHubAppInstallationsClient + { + private IGitHubAppInstallationsClient _client; + private readonly IConnection _connection; + + public ObservableGitHubAppInstallationsClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, nameof(client)); + + _client = client.GitHubApps.Installation; + _connection = client.Connection; + } + + /// + /// List repositories of the authenticated GitHub App Installation (requires GitHubApp Installation-Token auth). + /// + /// https://developer.github.com/v3/apps/installations/#list-repositories + public IObservable GetAllRepositoriesForCurrent() + { + return GetAllRepositoriesForCurrent(ApiOptions.None); + } + + /// + /// List repositories of the authenticated GitHub App Installation (requires GitHubApp Installation-Token auth). + /// + /// Options for changing the API response + /// https://developer.github.com/v3/apps/installations/#list-repositories + public IObservable GetAllRepositoriesForCurrent(ApiOptions options) + { + return _connection.GetAndFlattenAllPages(ApiUrls.InstallationRepositories(), null, AcceptHeaders.GitHubAppsPreview, options); + } + + /// + /// List repositories accessible to the user for an installation (requires GitHubApp User-To-Server Auth). + /// + /// The Id of the installation + /// https://developer.github.com/v3/apps/installations/#list-repositories-accessible-to-the-user-for-an-installation + public IObservable GetAllRepositoriesForCurrentUser(long installationId) + { + return GetAllRepositoriesForCurrentUser(installationId, ApiOptions.None); + } + + /// + /// List repositories accessible to the user for an installation (requires GitHubApp User-To-Server Auth). + /// + /// The Id of the installation + /// Options for changing the API response + /// https://developer.github.com/v3/apps/installations/#list-repositories-accessible-to-the-user-for-an-installation + public IObservable GetAllRepositoriesForCurrentUser(long installationId, ApiOptions options) + { + return _connection.GetAndFlattenAllPages(ApiUrls.UserInstallationRepositories(installationId), null, AcceptHeaders.GitHubAppsPreview, options); + } + } +} \ No newline at end of file diff --git a/Octokit.Reactive/Clients/ObservableGitHubAppsClient.cs b/Octokit.Reactive/Clients/ObservableGitHubAppsClient.cs index 6e52d538..5adc03ae 100644 --- a/Octokit.Reactive/Clients/ObservableGitHubAppsClient.cs +++ b/Octokit.Reactive/Clients/ObservableGitHubAppsClient.cs @@ -1,11 +1,11 @@ -using Octokit.Reactive.Internal; -using System; +using System; using System.Reactive.Threading.Tasks; +using Octokit.Reactive.Internal; namespace Octokit.Reactive { /// - /// A client for GitHub Applications API. Provides the methods required to get GitHub applications and installations. + /// A client for GitHub Applications API. /// /// /// See the GitHub Apps API documentation for more information. @@ -19,17 +19,34 @@ namespace Octokit.Reactive { Ensure.ArgumentNotNull(client, "client"); + Installation = new ObservableGitHubAppInstallationsClient(client); + _client = client.GitHubApps; _connection = client.Connection; } + /// + /// Access GitHub's Apps Installations API. + /// + /// + /// Refer to the API documentation for more information: https://developer.github.com/v3/apps/installations/ + /// + public IObservableGitHubAppInstallationsClient Installation { get; private set; } + + /// + /// Get a single GitHub App (if private, requires Personal Access Token or GitHubApp auth) + /// + /// https://developer.github.com/v3/apps/#get-a-single-github-app + /// The URL-friendly name of your GitHub App. You can find this on the settings page for your GitHub App. public IObservable Get(string slug) { + Ensure.ArgumentNotNullOrEmptyString(slug, nameof(slug)); + return _client.Get(slug).ToObservable(); } /// - /// Returns the GitHub App associated with the authentication credentials used (requires GitHubApp JWT token auth). + /// Returns the GitHub App associated with the authentication credentials used (requires GitHubApp auth). /// /// https://developer.github.com/v3/apps/#get-the-authenticated-github-app public IObservable GetCurrent() @@ -38,16 +55,16 @@ namespace Octokit.Reactive } /// - /// List installations of the authenticated GitHub App (requires GitHubApp JWT token auth). + /// List installations of the authenticated GitHub App (requires GitHubApp auth). /// /// https://developer.github.com/v3/apps/#find-installations public IObservable GetAllInstallationsForCurrent() { - return _connection.GetAndFlattenAllPages(ApiUrls.Installations(), null, AcceptHeaders.GitHubAppsPreview); + return GetAllInstallationsForCurrent(ApiOptions.None); } /// - /// List installations of the authenticated GitHub App (requires GitHubApp JWT token auth). + /// List installations of the authenticated GitHub App (requires GitHubApp auth). /// /// Options for changing the API response /// https://developer.github.com/v3/apps/#find-installations @@ -59,17 +76,46 @@ namespace Octokit.Reactive } /// - /// Get a single GitHub App Installation (requires GitHubApp JWT token auth). + /// Get a single GitHub App Installation (requires GitHubApp auth). /// /// https://developer.github.com/v3/apps/#get-a-single-installation /// The Id of the GitHub App Installation + [Obsolete("This method will be removed in a future release. Please use GetInstallationForCurrent() instead")] public IObservable GetInstallation(long installationId) { - return _client.GetInstallation(installationId).ToObservable(); + return GetInstallationForCurrent(installationId); } /// - /// Create a time bound access token for a GitHubApp Installation that can be used to access other API endpoints (requires GitHubApp JWT token auth). + /// Get a single GitHub App Installation (requires GitHubApp auth). + /// + /// https://developer.github.com/v3/apps/#get-a-single-installation + /// The Id of the GitHub App Installation + public IObservable GetInstallationForCurrent(long installationId) + { + return _client.GetInstallationForCurrent(installationId).ToObservable(); + } + + /// + /// List installations for the currently authenticated user (requires GitHubApp User-To-Server Auth). + /// + /// https://developer.github.com/v3/apps/#list-installations-for-user + public IObservable GetAllInstallationsForCurrentUser() + { + return _connection.GetAndFlattenAllPages(ApiUrls.UserInstallations(), null, AcceptHeaders.GitHubAppsPreview); + } + + /// + /// List installations for the currently authenticated user (requires GitHubApp User-To-Server Auth). + /// + /// https://developer.github.com/v3/apps/#list-installations-for-user + public IObservable GetAllInstallationsForCurrentUser(ApiOptions options) + { + return _connection.GetAndFlattenAllPages(ApiUrls.UserInstallations(), null, AcceptHeaders.GitHubAppsPreview, options); + } + + /// + /// Create a time bound access token for a GitHubApp Installation that can be used to access other API endpoints (requires GitHubApp auth). /// /// /// https://developer.github.com/v3/apps/#create-a-new-installation-token @@ -81,5 +127,53 @@ namespace Octokit.Reactive { return _client.CreateInstallationToken(installationId).ToObservable(); } + + /// + /// Enables an authenticated GitHub App to find the organization's installation information (requires GitHubApp auth). + /// + /// https://developer.github.com/v3/apps/#find-organization-installation + /// The name of the organization + public IObservable GetOrganizationInstallationForCurrent(string organization) + { + Ensure.ArgumentNotNullOrEmptyString(organization, nameof(organization)); + + return _client.GetOrganizationInstallationForCurrent(organization).ToObservable(); + } + + /// + /// Enables an authenticated GitHub App to find the repository's installation information (requires GitHubApp auth). + /// + /// https://developer.github.com/v3/apps/#find-repository-installation + /// The owner of the repo + /// The name of the repo + public IObservable GetRepositoryInstallationForCurrent(string owner, string repo) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(repo, nameof(repo)); + + return _client.GetRepositoryInstallationForCurrent(owner, repo).ToObservable(); + } + + /// + /// Enables an authenticated GitHub App to find the repository's installation information (requires GitHubApp auth). + /// + /// https://developer.github.com/v3/apps/#find-repository-installation + /// The Id of the repository + public IObservable GetRepositoryInstallationForCurrent(long repositoryId) + { + return _client.GetRepositoryInstallationForCurrent(repositoryId).ToObservable(); + } + + /// + /// Enables an authenticated GitHub App to find the users's installation information (requires GitHubApp auth). + /// + /// https://developer.github.com/v3/apps/#find-user-installation + /// The name of the user + public IObservable GetUserInstallationForCurrent(string user) + { + Ensure.ArgumentNotNullOrEmptyString(user, nameof(user)); + + return _client.GetUserInstallationForCurrent(user).ToObservable(); + } } } diff --git a/Octokit.Tests.Integration/Clients/GitHubAppInstallationsClientTests.cs b/Octokit.Tests.Integration/Clients/GitHubAppInstallationsClientTests.cs new file mode 100644 index 00000000..7260bc37 --- /dev/null +++ b/Octokit.Tests.Integration/Clients/GitHubAppInstallationsClientTests.cs @@ -0,0 +1,52 @@ +using System.Threading.Tasks; +using Octokit.Tests.Integration; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class GitHubAppInstallationsClientTests + { + public class TheGetAllRepositoriesForCurrentMethod + { + IGitHubClient _github; + + public TheGetAllRepositoriesForCurrentMethod() + { + // Authenticate as a GitHubApp Installation + _github = Helper.GetAuthenticatedGitHubAppInstallationForOwner(Helper.UserName); + } + + [GitHubAppsTest] + public async Task GetsAllRepositories() + { + var result = await _github.GitHubApps.Installation.GetAllRepositoriesForCurrent(); + + Assert.NotNull(result); + Assert.True(result.TotalCount > 0); + } + } + + public class TheGetAllRepositoriesForCurrentUserMethod + { + IGitHubClient _github; + + public TheGetAllRepositoriesForCurrentUserMethod() + { + // Need to Authenticate as User to Server but not possible without receiving redirect from github.com + //_github = Helper.GetAuthenticatedUserToServer(); + _github = null; + } + + [GitHubAppsTest(Skip = "Not possible to authenticate with User to Server auth")] + public async Task GetsAllRepositories() + { + var installationId = Helper.GetGitHubAppInstallationForOwner(Helper.UserName).Id; + + var result = await _github.GitHubApps.Installation.GetAllRepositoriesForCurrentUser(installationId); + + Assert.NotNull(result); + Assert.True(result.TotalCount > 0); + } + } + } +} \ No newline at end of file diff --git a/Octokit.Tests.Integration/Clients/GitHubAppsClientTests.cs b/Octokit.Tests.Integration/Clients/GitHubAppsClientTests.cs index 84c416ea..55b8066c 100644 --- a/Octokit.Tests.Integration/Clients/GitHubAppsClientTests.cs +++ b/Octokit.Tests.Integration/Clients/GitHubAppsClientTests.cs @@ -1,5 +1,4 @@ -using Octokit.Tests.Integration.Helpers; -using System; +using System; using System.Linq; using System.Threading.Tasks; using Xunit; @@ -77,11 +76,11 @@ namespace Octokit.Tests.Integration.Clients } } - public class TheGetInstallationMethod + public class TheGetInstallationForCurrentMethod { IGitHubClient _github; - public TheGetInstallationMethod() + public TheGetInstallationForCurrentMethod() { // Authenticate as a GitHubApp _github = Helper.GetAuthenticatedGitHubAppsClient(); @@ -94,7 +93,7 @@ namespace Octokit.Tests.Integration.Clients var installationId = Helper.GetGitHubAppInstallationForOwner(Helper.UserName).Id; // Get the installation by Id - var result = await _github.GitHubApps.GetInstallation(installationId); + var result = await _github.GitHubApps.GetInstallationForCurrent(installationId); Assert.True(result.AppId == Helper.GitHubAppId); Assert.Equal(Helper.GitHubAppId, result.AppId); @@ -106,6 +105,27 @@ namespace Octokit.Tests.Integration.Clients } } + public class TheGetAllInstallationsForCurrentUserMethod + { + IGitHubClient _github; + + public TheGetAllInstallationsForCurrentUserMethod() + { + // Need to Authenticate as User to Server but not possible without receiving redirect from github.com + //_github = Helper.GetAuthenticatedUserToServer(); + _github = null; + } + + [GitHubAppsTest(Skip ="Not possible to authenticate with User to Server auth")] + public async Task GetsAllInstallationsForCurrentUser() + { + var result = await _github.GitHubApps.GetAllInstallationsForCurrentUser(); + + Assert.NotNull(result); + Assert.True(result.TotalCount > 0); + } + } + public class TheCreateInstallationTokenMethod { IGitHubClient _github; @@ -129,5 +149,84 @@ namespace Octokit.Tests.Integration.Clients Assert.True(DateTimeOffset.Now < result.ExpiresAt); } } + + public class TheGetOrganizationInstallationForCurrentMethod + { + IGitHubClient _github; + + public TheGetOrganizationInstallationForCurrentMethod() + { + // Authenticate as a GitHubApp + _github = Helper.GetAuthenticatedGitHubAppsClient(); + } + + [GitHubAppsTest] + public async Task GetsOrganizationInstallations() + { + var result = await _github.GitHubApps.GetOrganizationInstallationForCurrent(Helper.Organization); + + Assert.NotNull(result); + } + } + + public class TheGetRepositoryInstallationForCurrentMethod + { + IGitHubClient _github; + IGitHubClient _githubAppInstallation; + + public TheGetRepositoryInstallationForCurrentMethod() + { + // Autheticate as a GitHubApp + _github = Helper.GetAuthenticatedGitHubAppsClient(); + + // Authenticate as a GitHubApp Installation + _githubAppInstallation = Helper.GetAuthenticatedGitHubAppInstallationForOwner(Helper.UserName); + } + + [GitHubAppsTest] + public async Task GetsRepositoryInstallations() + { + // Find a repo under the installation + var repos = await _githubAppInstallation.GitHubApps.Installation.GetAllRepositoriesForCurrent(); + var repo = repos.Repositories.First(); + + // Now, using the GitHub App auth, find this repository installation + var result = await _github.GitHubApps.GetRepositoryInstallationForCurrent(repo.Owner.Login, repo.Name); + + Assert.NotNull(result); + } + + [GitHubAppsTest] + public async Task GetsRepositoryInstallationsWithRepositoryId() + { + // Find a repo under the installation + var repos = await _githubAppInstallation.GitHubApps.Installation.GetAllRepositoriesForCurrent(); + var repo = repos.Repositories.First(); + + // Now, using the GitHub App auth, find this repository installation + var result = await _github.GitHubApps.GetRepositoryInstallationForCurrent(repo.Id); + + Assert.NotNull(result); + } + } + + public class TheGetUserInstallationForCurrentMethod + { + IGitHubClient _github; + + public TheGetUserInstallationForCurrentMethod() + { + // Authenticate as a GitHubApp + _github = Helper.GetAuthenticatedGitHubAppsClient(); + } + + [GitHubAppsTest] + public async Task GetsUserInstallations() + { + var result = await _github.GitHubApps.GetUserInstallationForCurrent(Helper.UserName); + + Assert.NotNull(result); + } + } } } diff --git a/Octokit.Tests.Integration/Reactive/ObservableGitHubAppInstallationsClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableGitHubAppInstallationsClientTests.cs new file mode 100644 index 00000000..41609fa0 --- /dev/null +++ b/Octokit.Tests.Integration/Reactive/ObservableGitHubAppInstallationsClientTests.cs @@ -0,0 +1,54 @@ +using System.Reactive.Linq; +using System.Threading.Tasks; +using Octokit.Reactive; +using Octokit.Tests.Integration; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class ObservableGitHubAppInstallationsClientTests + { + public class TheGetAllRepositoriesForCurrentMethod + { + IObservableGitHubClient _github; + + public TheGetAllRepositoriesForCurrentMethod() + { + // Authenticate as a GitHubApp Installation + _github = new ObservableGitHubClient(Helper.GetAuthenticatedGitHubAppInstallationForOwner(Helper.UserName)); + } + + [GitHubAppsTest] + public async Task GetsAllRepositories() + { + var result = await _github.GitHubApps.Installation.GetAllRepositoriesForCurrent(); + + Assert.NotNull(result); + Assert.True(result.TotalCount > 0); + } + } + + public class TheGetAllRepositoriesForCurrentUserMethod + { + IObservableGitHubClient _github; + + public TheGetAllRepositoriesForCurrentUserMethod() + { + // Need to Authenticate as User to Server but not possible without receiving redirect from github.com + //_github = new ObservableGitHubClient(Helper.GetAuthenticatedUserToServer()); + _github = null; + } + + [GitHubAppsTest(Skip = "Not possible to authenticate with User to Server auth")] + public async Task GetsAllRepositories() + { + var installationId = Helper.GetGitHubAppInstallationForOwner(Helper.UserName).Id; + + var result = await _github.GitHubApps.Installation.GetAllRepositoriesForCurrentUser(installationId); + + Assert.NotNull(result); + Assert.True(result.TotalCount > 0); + } + } + } +} \ No newline at end of file diff --git a/Octokit.Tests.Integration/Reactive/ObservableGitHubAppsClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableGitHubAppsClientTests.cs new file mode 100644 index 00000000..f8537835 --- /dev/null +++ b/Octokit.Tests.Integration/Reactive/ObservableGitHubAppsClientTests.cs @@ -0,0 +1,234 @@ +using System; +using System.Linq; +using System.Reactive.Linq; +using System.Threading.Tasks; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Integration.Clients +{ + public class ObservableGitHubAppsClientTests + { + public class TheGetMethod + { + IObservableGitHubClient _github; + + public TheGetMethod() + { + // Regular authentication + _github = new ObservableGitHubClient(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 + { + IObservableGitHubClient _github; + + public TheGetCurrentMethod() + { + // Authenticate as a GitHubApp + _github = new ObservableGitHubClient(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 + { + IObservableGitHubClient _github; + + public TheGetAllInstallationsForCurrentMethod() + { + // Authenticate as a GitHubApp + _github = new ObservableGitHubClient(Helper.GetAuthenticatedGitHubAppsClient()); + } + + [GitHubAppsTest] + public async Task GetsAllInstallations() + { + var result = await _github.GitHubApps.GetAllInstallationsForCurrent().ToList(); + + 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 TheGetInstallationForCurrentMethod + { + IObservableGitHubClient _github; + + public TheGetInstallationForCurrentMethod() + { + // Authenticate as a GitHubApp + _github = new ObservableGitHubClient(Helper.GetAuthenticatedGitHubAppsClient()); + } + + [GitHubAppsTest] + public async Task GetsInstallation() + { + // Get the installation Id + var installationId = Helper.GetGitHubAppInstallationForOwner(Helper.UserName).Id; + + // Get the installation by Id + var result = await _github.GitHubApps.GetInstallationForCurrent(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 TheGetAllInstallationsForCurrentUserMethod + { + IObservableGitHubClient _github; + + public TheGetAllInstallationsForCurrentUserMethod() + { + // Need to Authenticate as User to Server but not possible without receiving redirect from github.com + //_github = new ObservableGitHubClient(Helper.GetAuthenticatedUserToServer()); + _github = null; + } + + [GitHubAppsTest(Skip = "Not possible to authenticate with User to Server auth")] + public async Task GetsAllInstallationsForCurrentUser() + { + var result = await _github.GitHubApps.GetAllInstallationsForCurrentUser(); + + Assert.NotNull(result); + Assert.True(result.TotalCount > 0); + } + } + + public class TheCreateInstallationTokenMethod + { + IObservableGitHubClient _github; + + public TheCreateInstallationTokenMethod() + { + // Authenticate as a GitHubApp + _github = new ObservableGitHubClient(Helper.GetAuthenticatedGitHubAppsClient()); + } + + [GitHubAppsTest] + public async Task CreatesInstallationToken() + { + // Get the installation Id + var installationId = Helper.GetGitHubAppInstallationForOwner(Helper.UserName).Id; + + // Create installation token + var result = await _github.GitHubApps.CreateInstallationToken(installationId); + + Assert.NotNull(result.Token); + Assert.True(DateTimeOffset.Now < result.ExpiresAt); + } + } + + public class TheGetOrganizationInstallationForCurrentMethod + { + IObservableGitHubClient _github; + + public TheGetOrganizationInstallationForCurrentMethod() + { + // Authenticate as a GitHubApp + _github = new ObservableGitHubClient(Helper.GetAuthenticatedGitHubAppsClient()); + } + + [GitHubAppsTest] + public async Task GetsOrganizationInstallations() + { + var result = await _github.GitHubApps.GetOrganizationInstallationForCurrent(Helper.Organization); + + Assert.NotNull(result); + } + } + + public class TheGetRepositoryInstallationForCurrentMethod + { + IObservableGitHubClient _github; + IObservableGitHubClient _githubAppInstallation; + + public TheGetRepositoryInstallationForCurrentMethod() + { + // Autheticate as a GitHubApp + _github = new ObservableGitHubClient(Helper.GetAuthenticatedGitHubAppsClient()); + + // Authenticate as a GitHubApp Installation + _githubAppInstallation = new ObservableGitHubClient(Helper.GetAuthenticatedGitHubAppInstallationForOwner(Helper.UserName)); + } + + [GitHubAppsTest] + public async Task GetsRepositoryInstallations() + { + // Find a repo under the installation + var repos = await _githubAppInstallation.GitHubApps.Installation.GetAllRepositoriesForCurrent(); + var repo = repos.Repositories.First(); + + // Now, using the GitHub App auth, find this repository installation + var result = await _github.GitHubApps.GetRepositoryInstallationForCurrent(repo.Owner.Login, repo.Name); + + Assert.NotNull(result); + } + + [GitHubAppsTest] + public async Task GetsRepositoryInstallationsWithRepositoryId() + { + // Find a repo under the installation + var repos = await _githubAppInstallation.GitHubApps.Installation.GetAllRepositoriesForCurrent(); + var repo = repos.Repositories.First(); + + // Now, using the GitHub App auth, find this repository installation + var result = await _github.GitHubApps.GetRepositoryInstallationForCurrent(repo.Id); + + Assert.NotNull(result); + } + } + + public class TheGetUserInstallationForCurrentMethod + { + IObservableGitHubClient _github; + + public TheGetUserInstallationForCurrentMethod() + { + // Authenticate as a GitHubApp + _github = new ObservableGitHubClient(Helper.GetAuthenticatedGitHubAppsClient()); + } + + [GitHubAppsTest] + public async Task GetsUserInstallations() + { + var result = await _github.GitHubApps.GetUserInstallationForCurrent(Helper.UserName); + + Assert.NotNull(result); + } + } + } +} diff --git a/Octokit.Tests/Clients/GitHubAppInstallationsClientTests.cs b/Octokit.Tests/Clients/GitHubAppInstallationsClientTests.cs new file mode 100644 index 00000000..61a6f055 --- /dev/null +++ b/Octokit.Tests/Clients/GitHubAppInstallationsClientTests.cs @@ -0,0 +1,100 @@ +using System; +using System.Linq; +using NSubstitute; +using Octokit.Clients; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class GitHubAppInstallationsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new GitHubAppInstallationsClient(null)); + } + } + + public class TheGetAllRepositoriesForCurrentMethod + { + [Fact] + public void GetsFromCorrectUrl() + { + var connection = Substitute.For(); + var client = new GitHubAppInstallationsClient(connection); + + client.GetAllRepositoriesForCurrent(); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "installation/repositories"), + null, + "application/vnd.github.machine-man-preview+json", + Args.ApiOptions); + } + + [Fact] + public void GetsFromCorrectUrllWithApiOptions() + { + var connection = Substitute.For(); + var client = new GitHubAppInstallationsClient(connection); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 1 + }; + + client.GetAllRepositoriesForCurrent(options); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "installation/repositories"), + null, + "application/vnd.github.machine-man-preview+json", + options); + } + } + + public class TheGetAllRepositoriesForCurrentUserMethod + { + [Fact] + public void GetsFromCorrectUrl() + { + var connection = Substitute.For(); + var client = new GitHubAppInstallationsClient(connection); + + client.GetAllRepositoriesForCurrentUser(1234); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "user/installations/1234/repositories"), + null, + "application/vnd.github.machine-man-preview+json", + Args.ApiOptions); + } + + [Fact] + public void GetsFromCorrectUrllWithApiOptions() + { + var connection = Substitute.For(); + var client = new GitHubAppInstallationsClient(connection); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 1 + }; + + client.GetAllRepositoriesForCurrentUser(1234, options); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "user/installations/1234/repositories"), + null, + "application/vnd.github.machine-man-preview+json", + options); + } + } + } +} \ No newline at end of file diff --git a/Octokit.Tests/Clients/GitHubAppsClientTests.cs b/Octokit.Tests/Clients/GitHubAppsClientTests.cs index e96166d4..bdd26c21 100644 --- a/Octokit.Tests/Clients/GitHubAppsClientTests.cs +++ b/Octokit.Tests/Clients/GitHubAppsClientTests.cs @@ -16,6 +16,38 @@ namespace Octokit.Tests.Clients } } + public class TheGetMethod + { + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new GitHubAppsClient(connection); + + await Assert.ThrowsAsync(() => client.Get(null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new GitHubAppsClient(connection); + + await Assert.ThrowsAsync(() => client.Get("")); + } + + [Fact] + public void GetsFromCorrectUrl() + { + var connection = Substitute.For(); + var client = new GitHubAppsClient(connection); + + client.Get("foobar"); + + connection.Received().Get(Arg.Is(u => u.ToString() == "apps/foobar"), null, "application/vnd.github.machine-man-preview+json"); + } + } + public class TheGetCurrentMethod { [Fact] @@ -49,10 +81,9 @@ namespace Octokit.Tests.Clients client.GetAllInstallationsForCurrent(); - connection.Received().GetAll(Arg.Is(u => u.ToString() == "app/installations"), null, "application/vnd.github.machine-man-preview+json"); + connection.Received().GetAll(Arg.Is(u => u.ToString() == "app/installations"), null, "application/vnd.github.machine-man-preview+json", Args.ApiOptions); } - [Fact] public void RequestsTheCorrectUrlWithApiOptions() { @@ -72,7 +103,7 @@ namespace Octokit.Tests.Clients } } - public class TheGetInstallationMethod + public class TheGetInstallationForCurrentMethod { [Fact] public void GetsFromCorrectUrl() @@ -80,12 +111,44 @@ namespace Octokit.Tests.Clients var connection = Substitute.For(); var client = new GitHubAppsClient(connection); - client.GetInstallation(123); + client.GetInstallationForCurrent(123); connection.Received().Get(Arg.Is(u => u.ToString() == "app/installations/123"), null, "application/vnd.github.machine-man-preview+json"); } } + public class TheGetAllInstallationsForCurrentUserMethod + { + [Fact] + public async Task GetsFromCorrectUrl() + { + var connection = Substitute.For(); + var client = new GitHubAppsClient(connection); + + await client.GetAllInstallationsForCurrentUser(); + + await connection.Received().GetAll(Arg.Is(u => u.ToString() == "user/installations"), null, "application/vnd.github.machine-man-preview+json"); + } + + [Fact] + public async Task GetsFromCorrectUrlWithOptions() + { + var connection = Substitute.For(); + var client = new GitHubAppsClient(connection); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 1 + }; + + await client.GetAllInstallationsForCurrentUser(options); + + await connection.Received().GetAll(Arg.Is(u => u.ToString() == "user/installations"), null, "application/vnd.github.machine-man-preview+json", options); + } + } + public class TheCreateInstallationTokenMethod { [Fact] @@ -101,5 +164,114 @@ namespace Octokit.Tests.Clients connection.Received().Post(Arg.Is(u => u.ToString() == "app/installations/3141/access_tokens"), string.Empty, "application/vnd.github.machine-man-preview+json"); } } + + public class TheGetOrganizationInstallationForCurrentMethod + { + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new GitHubAppsClient(connection); + + await Assert.ThrowsAsync(() => client.GetOrganizationInstallationForCurrent(null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new GitHubAppsClient(connection); + + await Assert.ThrowsAsync(() => client.GetOrganizationInstallationForCurrent("")); + } + + [Fact] + public void GetsFromCorrectUrl() + { + var connection = Substitute.For(); + var client = new GitHubAppsClient(connection); + + client.GetOrganizationInstallationForCurrent("ducks"); + + connection.Received().Get(Arg.Is(u => u.ToString() == "orgs/ducks/installation"), null, "application/vnd.github.machine-man-preview+json"); + } + } + + public class TheGetRepositoryInstallationForCurrentMethod + { + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new GitHubAppsClient(connection); + + await Assert.ThrowsAsync(() => client.GetRepositoryInstallationForCurrent(null, "ducks")); + await Assert.ThrowsAsync(() => client.GetRepositoryInstallationForCurrent("mighty", null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new GitHubAppsClient(connection); + + await Assert.ThrowsAsync(() => client.GetRepositoryInstallationForCurrent("", "ducks")); + await Assert.ThrowsAsync(() => client.GetRepositoryInstallationForCurrent("mighty", "")); + } + + [Fact] + public void GetsFromCorrectUrl() + { + var connection = Substitute.For(); + var client = new GitHubAppsClient(connection); + + client.GetRepositoryInstallationForCurrent("mighty", "ducks"); + + connection.Received().Get(Arg.Is(u => u.ToString() == "repos/mighty/ducks/installation"), null, "application/vnd.github.machine-man-preview+json"); + } + + [Fact] + public void GetsFromCorrectUrlByRepositoryId() + { + var connection = Substitute.For(); + var client = new GitHubAppsClient(connection); + + client.GetRepositoryInstallationForCurrent(1234); + + connection.Received().Get(Arg.Is(u => u.ToString() == "repositories/1234/installation"), null, "application/vnd.github.machine-man-preview+json"); + } + } + + public class TheGetUserInstallationForCurrentMethod + { + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new GitHubAppsClient(connection); + + await Assert.ThrowsAsync(() => client.GetUserInstallationForCurrent(null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new GitHubAppsClient(connection); + + await Assert.ThrowsAsync(() => client.GetUserInstallationForCurrent("")); + } + + [Fact] + public void GetsFromCorrectUrl() + { + var connection = Substitute.For(); + var client = new GitHubAppsClient(connection); + + client.GetUserInstallationForCurrent("ducks"); + + connection.Received().Get(Arg.Is(u => u.ToString() == "users/ducks/installation"), null, "application/vnd.github.machine-man-preview+json"); + } + } } } diff --git a/Octokit.Tests/Reactive/ObservableGitHubAppInstallationsClientTests.cs b/Octokit.Tests/Reactive/ObservableGitHubAppInstallationsClientTests.cs new file mode 100644 index 00000000..d73dbe72 --- /dev/null +++ b/Octokit.Tests/Reactive/ObservableGitHubAppInstallationsClientTests.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NSubstitute; +using Octokit.Clients; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class ObservableGitHubAppInstallationsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new ObservableGitHubAppInstallationsClient(null)); + } + } + + public class TheGetAllRepositoriesForCurrentMethod + { + [Fact] + public void GetsFromCorrectUrl() + { + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableGitHubAppInstallationsClient(gitHubClient); + + client.GetAllRepositoriesForCurrent(); + + connection.Received().Get>( + Arg.Is(u => u.ToString() == "installation/repositories"), + Args.EmptyDictionary, + "application/vnd.github.machine-man-preview+json"); + } + + [Fact] + public void GetsFromCorrectUrllWithApiOptions() + { + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableGitHubAppInstallationsClient(gitHubClient); + + var options = new ApiOptions + { + PageSize = 1 + }; + + client.GetAllRepositoriesForCurrent(options); + + connection.Received().Get>( + Arg.Is(u => u.ToString() == "installation/repositories"), + Arg.Is>(x => + x.Count == 1 + && x["per_page"] == "1"), + "application/vnd.github.machine-man-preview+json"); + } + } + + public class TheGetAllRepositoriesForCurrentUserMethod + { + [Fact] + public void GetsFromCorrectUrl() + { + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableGitHubAppInstallationsClient(gitHubClient); + + client.GetAllRepositoriesForCurrentUser(1234); + + connection.Received().Get>( + Arg.Is(u => u.ToString() == "user/installations/1234/repositories"), + Args.EmptyDictionary, + "application/vnd.github.machine-man-preview+json"); + } + + [Fact] + public void GetsFromCorrectUrllWithApiOptions() + { + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableGitHubAppInstallationsClient(gitHubClient); + + var options = new ApiOptions + { + PageSize = 1 + }; + + client.GetAllRepositoriesForCurrentUser(1234, options); + + connection.Received().Get>( + Arg.Is(u => u.ToString() == "user/installations/1234/repositories"), + Arg.Is>(x => + x.Count == 1 + && x["per_page"] == "1"), + "application/vnd.github.machine-man-preview+json"); + } + } + } +} \ No newline at end of file diff --git a/Octokit.Tests/Reactive/ObservableGitHubAppsClientTests.cs b/Octokit.Tests/Reactive/ObservableGitHubAppsClientTests.cs new file mode 100644 index 00000000..95ca8836 --- /dev/null +++ b/Octokit.Tests/Reactive/ObservableGitHubAppsClientTests.cs @@ -0,0 +1,295 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using NSubstitute; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class ObservableGitHubAppsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new ObservableGitHubAppsClient(null)); + } + } + + public class TheGetMethod + { + [Fact] + public void EnsuresNonNullArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservableGitHubAppsClient(gitHubClient); + + Assert.Throws(() => client.Get(null)); + } + + [Fact] + public void EnsuresNonEmptyArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservableGitHubAppsClient(gitHubClient); + + Assert.Throws(() => client.Get("")); + } + + [Fact] + public void GetsFromCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableGitHubAppsClient(gitHubClient); + + client.Get("foobar"); + + gitHubClient.GitHubApps.Received().Get("foobar"); + } + } + + public class TheGetCurrentMethod + { + [Fact] + public void GetsFromCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableGitHubAppsClient(gitHubClient); + + client.GetCurrent(); + + gitHubClient.GitHubApps.Received().GetCurrent(); + } + } + + public class TheGetAllInstallationsForCurrentMethod + { + [Fact] + public void EnsuresNonNullArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservableGitHubAppsClient(gitHubClient); + + Assert.Throws(() => client.GetAllInstallationsForCurrent(null)); + } + + [Fact] + public void RequestsCorrectUrl() + { + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableGitHubAppsClient(gitHubClient); + + client.GetAllInstallationsForCurrent(); + + connection.Received().Get>( + Arg.Is(u => u.ToString() == "app/installations"), + Args.EmptyDictionary, + "application/vnd.github.machine-man-preview+json"); + } + + [Fact] + public void RequestsTheCorrectUrlWithApiOptions() + { + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableGitHubAppsClient(gitHubClient); + + var options = new ApiOptions + { + PageSize = 1 + }; + + client.GetAllInstallationsForCurrent(options); + + connection.Received().Get>( + Arg.Is(u => u.ToString() == "app/installations"), + Arg.Is>(x => + x.Count == 1 + && x["per_page"] == "1"), + "application/vnd.github.machine-man-preview+json"); + } + } + + public class TheGetInstallationForCurrentMethod + { + [Fact] + public void GetsFromCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableGitHubAppsClient(gitHubClient); + + client.GetInstallationForCurrent(123); + + gitHubClient.GitHubApps.Received().GetInstallationForCurrent(123); + } + } + + public class TheGetAllInstallationsForCurrentUserMethod + { + [Fact] + public void GetsFromCorrectUrl() + { + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableGitHubAppsClient(gitHubClient); + + client.GetAllInstallationsForCurrentUser(); + + connection.Received().Get>( + Arg.Is(u => u.ToString() == "user/installations"), + null, + "application/vnd.github.machine-man-preview+json"); + } + + [Fact] + public void GetsFromCorrectUrlWithOptions() + { + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableGitHubAppsClient(gitHubClient); + + var options = new ApiOptions + { + PageSize = 1 + }; + + client.GetAllInstallationsForCurrentUser(options); + + connection.Received().Get>( + Arg.Is(u => u.ToString() == "user/installations"), + Arg.Is>(x => + x.Count == 1 + && x["per_page"] == "1"), + "application/vnd.github.machine-man-preview+json"); + } + } + + public class TheCreateInstallationTokenMethod + { + [Fact] + public void PostsToCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableGitHubAppsClient(gitHubClient); + + int fakeInstallationId = 3141; + + client.CreateInstallationToken(fakeInstallationId); + + gitHubClient.GitHubApps.Received().CreateInstallationToken(fakeInstallationId); + } + } + + public class TheGetOrganizationInstallationForCurrentMethod + { + [Fact] + public void EnsuresNonNullArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservableGitHubAppsClient(gitHubClient); + + Assert.Throws(() => client.GetOrganizationInstallationForCurrent(null)); + } + + [Fact] + public void EnsuresNonEmptyArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservableGitHubAppsClient(gitHubClient); + + Assert.Throws(() => client.GetOrganizationInstallationForCurrent("")); + } + + [Fact] + public void GetsFromCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableGitHubAppsClient(gitHubClient); + + client.GetOrganizationInstallationForCurrent("ducks"); + + gitHubClient.GitHubApps.Received().GetOrganizationInstallationForCurrent("ducks"); + } + } + + public class TheGetRepositoryInstallationForCurrentMethod + { + [Fact] + public void EnsuresNonNullArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservableGitHubAppsClient(gitHubClient); + + Assert.Throws(() => client.GetRepositoryInstallationForCurrent(null, "ducks")); + Assert.Throws(() => client.GetRepositoryInstallationForCurrent("mighty", null)); + } + + [Fact] + public void EnsuresNonEmptyArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservableGitHubAppsClient(gitHubClient); + + Assert.Throws(() => client.GetRepositoryInstallationForCurrent("", "ducks")); + Assert.Throws(() => client.GetRepositoryInstallationForCurrent("mighty", "")); + } + + [Fact] + public void GetsFromCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableGitHubAppsClient(gitHubClient); + + client.GetRepositoryInstallationForCurrent("mighty", "ducks"); + + gitHubClient.GitHubApps.Received().GetRepositoryInstallationForCurrent("mighty", "ducks"); + } + + [Fact] + public void GetsFromCorrectUrlByRepositoryId() + { + var gitHubClient = Substitute.For(); + var client = new ObservableGitHubAppsClient(gitHubClient); + + client.GetRepositoryInstallationForCurrent(1234); + + gitHubClient.GitHubApps.Received().GetRepositoryInstallationForCurrent(1234); + } + } + + public class TheGetUserInstallationForCurrentMethod + { + [Fact] + public void EnsuresNonNullArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservableGitHubAppsClient(gitHubClient); + + Assert.Throws(() => client.GetUserInstallationForCurrent(null)); + } + + [Fact] + public void EnsuresNonEmptyArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservableGitHubAppsClient(gitHubClient); + + Assert.Throws(() => client.GetUserInstallationForCurrent("")); + } + + [Fact] + public void GetsFromCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableGitHubAppsClient(gitHubClient); + + client.GetUserInstallationForCurrent("ducks"); + + gitHubClient.GitHubApps.Received().GetUserInstallationForCurrent("ducks"); + } + } + } +} diff --git a/Octokit.sln.DotSettings b/Octokit.sln.DotSettings index 6fba030f..50852c97 100644 --- a/Octokit.sln.DotSettings +++ b/Octokit.sln.DotSettings @@ -24,7 +24,9 @@ 1 1 True + NEVER False + NEVER False False True @@ -262,8 +264,13 @@ II.2.12 <HandlesEvent /> <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> + True True + True + True + True True + True True True \ No newline at end of file diff --git a/Octokit/Clients/GitHubAppInstallationsClient.cs b/Octokit/Clients/GitHubAppInstallationsClient.cs new file mode 100644 index 00000000..7f18705a --- /dev/null +++ b/Octokit/Clients/GitHubAppInstallationsClient.cs @@ -0,0 +1,70 @@ +using System.Linq; +using System.Threading.Tasks; + +namespace Octokit.Clients +{ + /// + /// A client for GitHub Applications Installations API. + /// + /// + /// See the GitHub Apps Installations API documentation for more information. + /// + public class GitHubAppInstallationsClient : ApiClient, IGitHubAppInstallationsClient + { + public GitHubAppInstallationsClient(IApiConnection apiConnection) : base(apiConnection) + { + } + + /// + /// List repositories of the authenticated GitHub App Installation (requires GitHubApp Installation-Token auth). + /// + /// https://developer.github.com/v3/apps/installations/#list-repositories + public Task GetAllRepositoriesForCurrent() + { + return GetAllRepositoriesForCurrent(ApiOptions.None); + } + + /// + /// List repositories of the authenticated GitHub App Installation (requires GitHubApp Installation-Token auth). + /// + /// Options for changing the API response + /// https://developer.github.com/v3/apps/installations/#list-repositories + public async Task GetAllRepositoriesForCurrent(ApiOptions options) + { + Ensure.ArgumentNotNull(options, nameof(options)); + + var results = await ApiConnection.GetAll(ApiUrls.InstallationRepositories(), null, AcceptHeaders.GitHubAppsPreview, options).ConfigureAwait(false); + + return new RepositoriesResponse( + results.Count > 0 ? results.Max(x => x.TotalCount) : 0, + results.SelectMany(x => x.Repositories).ToList()); + } + + /// + /// List repositories accessible to the user for an installation (requires GitHubApp User-To-Server Auth). + /// + /// The Id of the installation + /// https://developer.github.com/v3/apps/installations/#list-repositories-accessible-to-the-user-for-an-installation + public Task GetAllRepositoriesForCurrentUser(long installationId) + { + return GetAllRepositoriesForCurrentUser(installationId, ApiOptions.None); + } + + /// + /// List repositories accessible to the user for an installation (requires GitHubApp User-To-Server Auth). + /// + /// The Id of the installation + /// Options for changing the API response + /// https://developer.github.com/v3/apps/installations/#list-repositories-accessible-to-the-user-for-an-installation + public async Task GetAllRepositoriesForCurrentUser(long installationId, ApiOptions options) + { + Ensure.ArgumentNotNull(options, nameof(options)); + + var results = await ApiConnection.GetAll(ApiUrls.UserInstallationRepositories(installationId), null, AcceptHeaders.GitHubAppsPreview, options).ConfigureAwait(false); + + return new RepositoriesResponse( + results.Count > 0 ? results.Max(x => x.TotalCount) : 0, + results.SelectMany(x => x.Repositories).ToList()); + } + } +} \ No newline at end of file diff --git a/Octokit/Clients/GitHubAppsClient.cs b/Octokit/Clients/GitHubAppsClient.cs index 0a865fb1..ad33ae38 100644 --- a/Octokit/Clients/GitHubAppsClient.cs +++ b/Octokit/Clients/GitHubAppsClient.cs @@ -1,10 +1,13 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; +using Octokit.Clients; namespace Octokit { /// - /// A client for GitHub Applications API. Provides the methods required to get GitHub applications and installations. + /// A client for GitHub Applications API. /// /// /// See the GitHub Apps API documentation for more information. @@ -13,20 +16,33 @@ namespace Octokit { public GitHubAppsClient(IApiConnection apiConnection) : base(apiConnection) { + Ensure.ArgumentNotNull(apiConnection, nameof(apiConnection)); + + Installation = new GitHubAppInstallationsClient(apiConnection); } /// - /// Get a single GitHub App. + /// Access GitHub's Apps Installations API. + /// + /// + /// Refer to the API documentation for more information: https://developer.github.com/v3/apps/installations/ + /// + public IGitHubAppInstallationsClient Installation { get; } + + /// + /// Get a single GitHub App (if private, requires Personal Access Token or GitHubApp auth) /// /// https://developer.github.com/v3/apps/#get-a-single-github-app /// The URL-friendly name of your GitHub App. You can find this on the settings page for your GitHub App. public Task Get(string slug) { + Ensure.ArgumentNotNullOrEmptyString(slug, nameof(slug)); + return ApiConnection.Get(ApiUrls.App(slug), null, AcceptHeaders.GitHubAppsPreview); } /// - /// Returns the GitHub App associated with the authentication credentials used (requires GitHubApp JWT token auth). + /// Returns the GitHub App associated with the authentication credentials used (requires GitHubApp auth). /// /// https://developer.github.com/v3/apps/#get-the-authenticated-github-app public Task GetCurrent() @@ -35,16 +51,16 @@ namespace Octokit } /// - /// List installations of the authenticated GitHub App (requires GitHubApp JWT token auth). + /// List installations of the authenticated GitHub App (requires GitHubApp auth). /// /// https://developer.github.com/v3/apps/#find-installations public Task> GetAllInstallationsForCurrent() { - return ApiConnection.GetAll(ApiUrls.Installations(), null, AcceptHeaders.GitHubAppsPreview); + return GetAllInstallationsForCurrent(ApiOptions.None); } /// - /// List installations of the authenticated GitHub App (requires GitHubApp JWT token auth). + /// List installations of the authenticated GitHub App (requires GitHubApp auth). /// /// Options for changing the API response /// https://developer.github.com/v3/apps/#find-installations @@ -56,17 +72,56 @@ namespace Octokit } /// - /// Get a single GitHub App Installation (requires GitHubApp JWT token auth). + /// Get a single GitHub App Installation (requires GitHubApp auth). /// /// https://developer.github.com/v3/apps/#get-a-single-installation /// The Id of the GitHub App Installation + [Obsolete("This method will be removed in a future release. Please use GetInstallationForCurrent() instead")] public Task GetInstallation(long installationId) + { + return GetInstallationForCurrent(installationId); + } + + /// + /// Get a single GitHub App Installation (requires GitHubApp auth). + /// + /// https://developer.github.com/v3/apps/#get-a-single-installation + /// The Id of the GitHub App Installation + public Task GetInstallationForCurrent(long installationId) { return ApiConnection.Get(ApiUrls.Installation(installationId), null, AcceptHeaders.GitHubAppsPreview); } /// - /// Create a time bound access token for a GitHubApp Installation that can be used to access other API endpoints (requires GitHubApp JWT token auth). + /// List installations for the currently authenticated user (requires GitHubApp User-To-Server Auth). + /// + /// https://developer.github.com/v3/apps/#list-installations-for-user + public async Task GetAllInstallationsForCurrentUser() + { + var results = await ApiConnection.GetAll(ApiUrls.UserInstallations(), null, AcceptHeaders.GitHubAppsPreview).ConfigureAwait(false); + + return new InstallationsResponse( + results.Count > 0 ? results.Max(x => x.TotalCount) : 0, + results.SelectMany(x => x.Installations).ToList()); + } + + /// + /// List installations for the currently authenticated user (requires GitHubApp User-To-Server Auth). + /// + /// https://developer.github.com/v3/apps/#list-installations-for-user + public async Task GetAllInstallationsForCurrentUser(ApiOptions options) + { + Ensure.ArgumentNotNull(options, nameof(options)); + + var results = await ApiConnection.GetAll(ApiUrls.UserInstallations(), null, AcceptHeaders.GitHubAppsPreview, options).ConfigureAwait(false); + + return new InstallationsResponse( + results.Count > 0 ? results.Max(x => x.TotalCount) : 0, + results.SelectMany(x => x.Installations).ToList()); + } + + /// + /// Create a time bound access token for a GitHubApp Installation that can be used to access other API endpoints (requires GitHubApp auth). /// /// /// https://developer.github.com/v3/apps/#create-a-new-installation-token @@ -78,5 +133,53 @@ namespace Octokit { return ApiConnection.Post(ApiUrls.AccessTokens(installationId), string.Empty, AcceptHeaders.GitHubAppsPreview); } + + /// + /// Enables an authenticated GitHub App to find the organization's installation information (requires GitHubApp auth). + /// + /// https://developer.github.com/v3/apps/#find-organization-installation + /// The name of the organization + public Task GetOrganizationInstallationForCurrent(string organization) + { + Ensure.ArgumentNotNullOrEmptyString(organization, nameof(organization)); + + return ApiConnection.Get(ApiUrls.OrganizationInstallation(organization), null, AcceptHeaders.GitHubAppsPreview); + } + + /// + /// Enables an authenticated GitHub App to find the repository's installation information (requires GitHubApp auth). + /// + /// https://developer.github.com/v3/apps/#find-repository-installation + /// The owner of the repo + /// The name of the repo + public Task GetRepositoryInstallationForCurrent(string owner, string repo) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(repo, nameof(repo)); + + return ApiConnection.Get(ApiUrls.RepoInstallation(owner, repo), null, AcceptHeaders.GitHubAppsPreview); + } + + /// + /// Enables an authenticated GitHub App to find the repository's installation information (requires GitHubApp auth). + /// + /// https://developer.github.com/v3/apps/#find-repository-installation + /// The Id of the repository + public Task GetRepositoryInstallationForCurrent(long repositoryId) + { + return ApiConnection.Get(ApiUrls.RepoInstallation(repositoryId), null, AcceptHeaders.GitHubAppsPreview); + } + + /// + /// Enables an authenticated GitHub App to find the users's installation information (requires GitHubApp auth). + /// + /// https://developer.github.com/v3/apps/#find-user-installation + /// The name of the user + public Task GetUserInstallationForCurrent(string user) + { + Ensure.ArgumentNotNullOrEmptyString(user, nameof(user)); + + return ApiConnection.Get(ApiUrls.UserInstallation(user), null, AcceptHeaders.GitHubAppsPreview); + } } } \ No newline at end of file diff --git a/Octokit/Clients/IGitHubAppInstallationsClient.cs b/Octokit/Clients/IGitHubAppInstallationsClient.cs new file mode 100644 index 00000000..7e521090 --- /dev/null +++ b/Octokit/Clients/IGitHubAppInstallationsClient.cs @@ -0,0 +1,41 @@ +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub Applications Installations API. + /// + /// + /// See the GitHub Apps Installations API documentation for more information. + /// + public interface IGitHubAppInstallationsClient + { + /// + /// List repositories of the authenticated GitHub App Installation (requires GitHubApp Installation-Token auth). + /// + /// https://developer.github.com/v3/apps/installations/#list-repositories + Task GetAllRepositoriesForCurrent(); + + /// + /// List repositories of the authenticated GitHub App Installation (requires GitHubApp Installation-Token auth). + /// + /// Options for changing the API response + /// https://developer.github.com/v3/apps/installations/#list-repositories + Task GetAllRepositoriesForCurrent(ApiOptions options); + + /// + /// List repositories accessible to the user for an installation (requires GitHubApp User-To-Server Auth). + /// + /// The Id of the installation + /// https://developer.github.com/v3/apps/installations/#list-repositories-accessible-to-the-user-for-an-installation + Task GetAllRepositoriesForCurrentUser(long installationId); + + /// + /// List repositories accessible to the user for an installation (requires GitHubApp User-To-Server Auth). + /// + /// The Id of the installation + /// Options for changing the API response + /// https://developer.github.com/v3/apps/installations/#list-repositories-accessible-to-the-user-for-an-installation + Task GetAllRepositoriesForCurrentUser(long installationId, ApiOptions options); + } +} \ No newline at end of file diff --git a/Octokit/Clients/IGitHubAppsClient.cs b/Octokit/Clients/IGitHubAppsClient.cs index 71802151..4906a4fe 100644 --- a/Octokit/Clients/IGitHubAppsClient.cs +++ b/Octokit/Clients/IGitHubAppsClient.cs @@ -1,10 +1,11 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Threading.Tasks; namespace Octokit { /// - /// A client for GitHub Applications API. Provides the methods required to get GitHub applications and installations. + /// A client for GitHub Applications API. /// /// /// See the GitHub Apps API documentation for more information. @@ -12,40 +13,68 @@ namespace Octokit public interface IGitHubAppsClient { /// - /// Get a single GitHub App. + /// Access GitHub's Apps Installations API. + /// + /// + /// Refer to the API documentation for more information: https://developer.github.com/v3/apps/installations/ + /// + IGitHubAppInstallationsClient Installation { get; } + + /// + /// Get a single GitHub App (if private, requires Personal Access Token or GitHubApp auth) /// /// https://developer.github.com/v3/apps/#get-a-single-github-app /// The URL-friendly name of your GitHub App. You can find this on the settings page for your GitHub App. Task Get(string slug); /// - /// Returns the GitHub App associated with the authentication credentials used (requires GitHubApp JWT token auth). + /// Returns the GitHub App associated with the authentication credentials used (requires GitHubApp auth). /// /// https://developer.github.com/v3/apps/#get-the-authenticated-github-app Task GetCurrent(); /// - /// List installations of the authenticated GitHub App (requires GitHubApp JWT token auth). + /// List installations of the authenticated GitHub App (requires GitHubApp auth). /// /// https://developer.github.com/v3/apps/#find-installations Task> GetAllInstallationsForCurrent(); /// - /// List installations of the authenticated GitHub App (requires GitHubApp JWT token auth). + /// List installations of the authenticated GitHub App (requires GitHubApp auth). /// /// Options for changing the API response /// https://developer.github.com/v3/apps/#find-installations Task> GetAllInstallationsForCurrent(ApiOptions options); /// - /// Get a single GitHub App Installation (requires GitHubApp JWT token auth). + /// Get a single GitHub App Installation (requires GitHubApp auth). /// /// https://developer.github.com/v3/apps/#get-a-single-installation /// The Id of the GitHub App Installation + [Obsolete("This method will be removed in a future release. Please use GetInstallationForCurrent() instead")] Task GetInstallation(long installationId); /// - /// Create a time bound access token for a GitHubApp Installation that can be used to access other API endpoints (requires GitHubApp JWT token auth). + /// Get a single GitHub App Installation (requires GitHubApp auth). + /// + /// https://developer.github.com/v3/apps/#get-a-single-installation + /// The Id of the GitHub App Installation + Task GetInstallationForCurrent(long installationId); + + /// + /// List installations for the currently authenticated user (requires GitHubApp User-To-Server Auth). + /// + /// https://developer.github.com/v3/apps/#list-installations-for-user + Task GetAllInstallationsForCurrentUser(); + + /// + /// List installations for the currently authenticated user (requires GitHubApp User-To-Server Auth). + /// + /// https://developer.github.com/v3/apps/#list-installations-for-user + Task GetAllInstallationsForCurrentUser(ApiOptions options); + + /// + /// Create a time bound access token for a GitHubApp Installation that can be used to access other API endpoints (requires GitHubApp auth). /// /// /// https://developer.github.com/v3/apps/#create-a-new-installation-token @@ -54,5 +83,34 @@ namespace Octokit /// /// The Id of the GitHub App Installation Task CreateInstallationToken(long installationId); + + /// + /// Enables an authenticated GitHub App to find the organization's installation information (requires GitHubApp auth). + /// + /// https://developer.github.com/v3/apps/#find-organization-installation + /// The name of the organization + Task GetOrganizationInstallationForCurrent(string organization); + + /// + /// Enables an authenticated GitHub App to find the repository's installation information (requires GitHubApp auth). + /// + /// https://developer.github.com/v3/apps/#find-repository-installation + /// The owner of the repo + /// The name of the repo + Task GetRepositoryInstallationForCurrent(string owner, string repo); + + /// + /// Enables an authenticated GitHub App to find the repository's installation information (requires GitHubApp auth). + /// + /// https://developer.github.com/v3/apps/#find-repository-installation + /// The Id of the repository + Task GetRepositoryInstallationForCurrent(long repositoryId); + + /// + /// Enables an authenticated GitHub App to find the users's installation information (requires GitHubApp auth). + /// + /// https://developer.github.com/v3/apps/#find-user-installation + /// The name of the user + Task GetUserInstallationForCurrent(string user); } } \ No newline at end of file diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index 9fe5dd7c..1de14ddd 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -309,21 +309,79 @@ namespace Octokit /// /// Returns the that returns all the installations of the authenticated application. /// - /// public static Uri Installations() { return "app/installations".FormatUri(); } /// - /// Returns the that returns a single installation of the authenticated application. + /// Returns the that lists repositories accessible to the user for an installation.. + /// + /// The id of the installation + public static Uri UserInstallationRepositories(long installationId) + { + return "user/installations/{0}/repositories".FormatUri(installationId); + } + + /// + /// Returns the that returns the repository's installation information. /// /// + public static Uri RepoInstallation(string owner, string repo) + { + return "repos/{0}/{1}/installation".FormatUri(owner, repo); + } + + /// + /// Returns the that returns the repository's installation information. + /// + /// + public static Uri RepoInstallation(long repositoryId) + { + return "repositories/{0}/installation".FormatUri(repositoryId); + } + + /// + /// Returns the that returns the organization's installation information. + /// + public static Uri OrganizationInstallation(string organization) + { + return "orgs/{0}/installation".FormatUri(organization); ; + } + + /// + /// Returns the that returns the user's installation information. + /// + public static Uri UserInstallation(string username) + { + return "users/{0}/installation".FormatUri(username); + } + + /// + /// Returns the that returns a single installation of the authenticated application. + /// public static Uri Installation(long installationId) { return "app/installations/{0}".FormatUri(installationId); } + /// + /// Returns the that returns all the installations in repositories the user has explicit permission to access + /// + public static Uri UserInstallations() + { + return "user/installations".FormatUri(); + } + + /// + /// Returns the that returns all the repositores + /// + /// + public static Uri InstallationRepositories() + { + return "installation/repositories".FormatUri(); + } + /// /// Returns the that returns all of the issues across all the authenticated user’s visible /// repositories including owned repositories, member repositories, and organization repositories: diff --git a/Octokit/Models/Response/InstallationsResponse.cs b/Octokit/Models/Response/InstallationsResponse.cs new file mode 100644 index 00000000..acea8ec7 --- /dev/null +++ b/Octokit/Models/Response/InstallationsResponse.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class InstallationsResponse + { + public InstallationsResponse() + { + } + + public InstallationsResponse(int totalCount, IReadOnlyList installations) + { + TotalCount = totalCount; + Installations = installations; + } + + /// + /// The total number of check suites that match the request filter + /// + public int TotalCount { get; protected set; } + + /// + /// The retrieved check suites + /// + public IReadOnlyList Installations { get; protected set; } + + internal string DebuggerDisplay => string.Format(CultureInfo.CurrentCulture, "TotalCount: {0}, Installations: {1}", TotalCount, Installations.Count); + } +} \ No newline at end of file diff --git a/Octokit/Models/Response/RepositoriesResponse.cs b/Octokit/Models/Response/RepositoriesResponse.cs new file mode 100644 index 00000000..dd1f7833 --- /dev/null +++ b/Octokit/Models/Response/RepositoriesResponse.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class RepositoriesResponse + { + public RepositoriesResponse() + { + } + + public RepositoriesResponse(int totalCount, IReadOnlyList repositories) + { + TotalCount = totalCount; + Repositories = repositories; + } + + /// + /// The total number of check suites that match the request filter + /// + public int TotalCount { get; protected set; } + + /// + /// The retrieved check suites + /// + public IReadOnlyList Repositories { get; protected set; } + + internal string DebuggerDisplay => string.Format(CultureInfo.CurrentCulture, "TotalCount: {0}, Repositories: {1}", TotalCount, Repositories.Count); + } +} \ No newline at end of file