diff --git a/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseClient.cs b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseClient.cs index 4a494899ca..72e2d25efe 100644 --- a/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseClient.cs +++ b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseClient.cs @@ -15,5 +15,21 @@ public interface IObservableEnterpriseClient /// See the Enterprise Admin Stats API documentation for more information. /// IObservableEnterpriseAdminStatsClient AdminStats { get; } + + /// + /// A client for GitHub's Enterprise License API + /// + /// + /// See the Enterprise License API documentation for more information. + /// + IObservableEnterpriseLicenseClient License { get; } + + /// + /// A client for GitHub's Enterprise Organization API + /// + /// + /// See the Enterprise Organization API documentation for more information. + /// + IObservableEnterpriseOrganizationClient Organization { get; } } } diff --git a/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLicenseClient.cs b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLicenseClient.cs new file mode 100644 index 0000000000..7ec56f1b11 --- /dev/null +++ b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLicenseClient.cs @@ -0,0 +1,25 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reactive.Threading.Tasks; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Enterprise License API + /// + /// + /// See the Enterprise License API documentation for more information. + /// + public interface IObservableEnterpriseLicenseClient + { + /// + /// Gets GitHub Enterprise License Information (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/license/#get-license-information + /// + /// The statistics. + [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get")] + IObservable Get(); + } +} diff --git a/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseOrganizationClient.cs b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseOrganizationClient.cs new file mode 100644 index 0000000000..38f744f1c5 --- /dev/null +++ b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseOrganizationClient.cs @@ -0,0 +1,25 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reactive.Threading.Tasks; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Enterprise Organization API + /// + /// + /// See the Enterprise Organization API documentation for more information. + /// + public interface IObservableEnterpriseOrganizationClient + { + /// + /// Creates an Organization on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/orgs/#create-an-organization + /// + /// A instance describing the organization to be created + /// The created. + IObservable Create(NewOrganization newOrganization); + } +} diff --git a/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseClient.cs b/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseClient.cs index 8466e9b85a..7a52afcd5b 100644 --- a/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseClient.cs +++ b/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseClient.cs @@ -13,6 +13,8 @@ public ObservableEnterpriseClient(IGitHubClient client) Ensure.ArgumentNotNull(client, "client"); AdminStats = new ObservableEnterpriseAdminStatsClient(client); + License = new ObservableEnterpriseLicenseClient(client); + Organization = new ObservableEnterpriseOrganizationClient(client); } /// @@ -22,5 +24,21 @@ public ObservableEnterpriseClient(IGitHubClient client) /// See the Enterprise Admin Stats API documentation for more information. /// public IObservableEnterpriseAdminStatsClient AdminStats { get; private set; } + + /// + /// A client for GitHub's Enterprise License API + /// + /// + /// See the Enterprise License API documentation for more information. + /// + public IObservableEnterpriseLicenseClient License { get; private set; } + + /// + /// A client for GitHub's Enterprise Organization API + /// + /// + /// See the Enterprise Organization API documentation for more information. + /// + public IObservableEnterpriseOrganizationClient Organization { get; private set; } } } diff --git a/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLicenseClient.cs b/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLicenseClient.cs new file mode 100644 index 0000000000..bf85248392 --- /dev/null +++ b/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLicenseClient.cs @@ -0,0 +1,37 @@ +using System; +using System.Reactive.Threading.Tasks; +using Octokit; + + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Enterprise License API + /// + /// + /// See the Enterprise License API documentation for more information. + /// + public class ObservableEnterpriseLicenseClient : IObservableEnterpriseLicenseClient + { + readonly IEnterpriseLicenseClient _client; + + public ObservableEnterpriseLicenseClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, "client"); + + _client = client.Enterprise.License; + } + + /// + /// Gets GitHub Enterprise License Information (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/license/#get-license-information + /// + /// The statistics. + public IObservable Get() + { + return _client.Get().ToObservable(); + } + } +} diff --git a/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseOrganizationClient.cs b/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseOrganizationClient.cs new file mode 100644 index 0000000000..1f78574c2b --- /dev/null +++ b/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseOrganizationClient.cs @@ -0,0 +1,38 @@ +using System; +using System.Reactive.Threading.Tasks; +using Octokit; + + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Enterprise Organization API + /// + /// + /// See the Enterprise Organization API documentation for more information. + /// + public class ObservableEnterpriseOrganizationClient : IObservableEnterpriseOrganizationClient + { + readonly IEnterpriseOrganizationClient _client; + + public ObservableEnterpriseOrganizationClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, "client"); + + _client = client.Enterprise.Organization; + } + + /// + /// Creates an Organization on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/orgs/#create-an-organization + /// + /// A instance describing the organization to be created + /// The created. + public IObservable Create(NewOrganization newOrganization) + { + return _client.Create(newOrganization).ToObservable(); + } + } +} diff --git a/Octokit.Reactive/Octokit.Reactive-Mono.csproj b/Octokit.Reactive/Octokit.Reactive-Mono.csproj index 8ca3abddd0..3c0f28aa59 100644 --- a/Octokit.Reactive/Octokit.Reactive-Mono.csproj +++ b/Octokit.Reactive/Octokit.Reactive-Mono.csproj @@ -163,6 +163,10 @@ + + + + diff --git a/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj b/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj index ad9522c1c9..a808ba9188 100644 --- a/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj +++ b/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj @@ -171,6 +171,10 @@ + + + + diff --git a/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj b/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj index 21f480080e..616d3ad0d3 100644 --- a/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj +++ b/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj @@ -167,6 +167,10 @@ + + + + diff --git a/Octokit.Reactive/Octokit.Reactive.csproj b/Octokit.Reactive/Octokit.Reactive.csproj index 618f9d2610..0841fbb46e 100644 --- a/Octokit.Reactive/Octokit.Reactive.csproj +++ b/Octokit.Reactive/Octokit.Reactive.csproj @@ -75,10 +75,14 @@ Properties\SolutionInfo.cs + + + + diff --git a/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseAdminStatsClientTests.cs b/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseAdminStatsClientTests.cs index 12dc638ae9..2c8b1cf6d9 100644 --- a/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseAdminStatsClientTests.cs +++ b/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseAdminStatsClientTests.cs @@ -9,7 +9,7 @@ public class EnterpriseAdminStatsClientTests public EnterpriseAdminStatsClientTests() { - _github = Helper.GetAuthenticatedClient(); + _github = EnterpriseHelper.GetAuthenticatedClient(); } [GitHubEnterpriseTest] diff --git a/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLicenseClientTests.cs b/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLicenseClientTests.cs new file mode 100644 index 0000000000..81cd97b4f7 --- /dev/null +++ b/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLicenseClientTests.cs @@ -0,0 +1,23 @@ +using System.Threading.Tasks; +using Octokit; +using Octokit.Tests.Integration; +using Xunit; + +public class EnterpriseLicenseClientTests +{ + readonly IGitHubClient _github; + + public EnterpriseLicenseClientTests() + { + _github = EnterpriseHelper.GetAuthenticatedClient(); + } + + [GitHubEnterpriseTest] + public async Task CanGetLicense() + { + var licenseInfo = await + _github.Enterprise.License.Get(); + + Assert.NotNull(licenseInfo); + } +} \ No newline at end of file diff --git a/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseOrganizationClientTests.cs b/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseOrganizationClientTests.cs new file mode 100644 index 0000000000..ce77ae3b46 --- /dev/null +++ b/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseOrganizationClientTests.cs @@ -0,0 +1,34 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Octokit; +using Octokit.Tests.Integration; +using Xunit; + +public class EnterpriseOrganizationClientTests +{ + readonly IGitHubClient _github; + + public EnterpriseOrganizationClientTests() + { + _github = EnterpriseHelper.GetAuthenticatedClient(); + } + + [GitHubEnterpriseTest] + public async Task CanCreateOrganization() + { + string orgLogin = Helper.MakeNameWithTimestamp("MyOrganization"); + string orgName = String.Concat(orgLogin, " Display Name"); + + var newOrganization = new NewOrganization(orgLogin, EnterpriseHelper.UserName, orgName); + var organization = await + _github.Enterprise.Organization.Create(newOrganization); + + Assert.NotNull(organization); + + // Get organization and check login/name + var checkOrg = await _github.Organization.Get(orgLogin); + Assert.Equal(checkOrg.Login, orgLogin); + Assert.Equal(checkOrg.Name, orgName); + } +} diff --git a/Octokit.Tests.Integration/EnterpriseHelper.cs b/Octokit.Tests.Integration/EnterpriseHelper.cs new file mode 100644 index 0000000000..9a1ab54e06 --- /dev/null +++ b/Octokit.Tests.Integration/EnterpriseHelper.cs @@ -0,0 +1,164 @@ +using System; +using System.Diagnostics; +using System.IO; + +namespace Octokit.Tests.Integration +{ + public static class EnterpriseHelper + { + static readonly Lazy _credentialsThunk = new Lazy(() => + { + var githubUsername = Environment.GetEnvironmentVariable("OCTOKIT_GHE_USERNAME"); + UserName = githubUsername; + Organization = Environment.GetEnvironmentVariable("OCTOKIT_GHE_ORGANIZATION"); + + var githubToken = Environment.GetEnvironmentVariable("OCTOKIT_GHE_OAUTHTOKEN"); + + if (githubToken != null) + return new Credentials(githubToken); + + var githubPassword = Environment.GetEnvironmentVariable("OCTOKIT_GHE_PASSWORD"); + + if (githubUsername == null || githubPassword == null) + return null; + + return new Credentials(githubUsername, githubPassword); + }); + + static readonly Lazy _oauthApplicationCredentials = new Lazy(() => + { + var applicationClientId = ClientId; + var applicationClientSecret = ClientSecret; + + if (applicationClientId == null || applicationClientSecret == null) + return null; + + return new Credentials(applicationClientId, applicationClientSecret); + }); + + static readonly Lazy _basicAuthCredentials = new Lazy(() => + { + var githubUsername = Environment.GetEnvironmentVariable("OCTOKIT_GHE_USERNAME"); + UserName = githubUsername; + Organization = Environment.GetEnvironmentVariable("OCTOKIT_GHE_ORGANIZATION"); + + var githubPassword = Environment.GetEnvironmentVariable("OCTOKIT_GHE_PASSWORD"); + + if (githubUsername == null || githubPassword == null) + return null; + + return new Credentials(githubUsername, githubPassword); + }); + + static readonly Lazy _gitHubEnterpriseEnabled = new Lazy(() => + { + string enabled = Environment.GetEnvironmentVariable("OCTOKIT_GHE_ENABLED"); + return !String.IsNullOrWhiteSpace(enabled); + }); + + static readonly Lazy _gitHubEnterpriseUrl = new Lazy(() => + { + string uri = Environment.GetEnvironmentVariable("OCTOKIT_GHE_URL"); + + if (uri != null) + return new Uri(uri); + + return null; + }); + + static EnterpriseHelper() + { + // Force reading of environment variables. + // This wasn't happening if UserName/Organization were + // retrieved before Credentials. + Debug.WriteIf(Credentials == null, "No credentials specified."); + } + + public static string UserName { get; private set; } + public static string Organization { get; private set; } + + /// + /// These credentials should be set to a test GitHub account using the powershell script configure-integration-tests.ps1 + /// + public static Credentials Credentials { get { return _credentialsThunk.Value; } } + + public static Credentials ApplicationCredentials { get { return _oauthApplicationCredentials.Value; } } + + public static Credentials BasicAuthCredentials { get { return _basicAuthCredentials.Value; } } + + public static bool IsGitHubEnterpriseEnabled { get { return _gitHubEnterpriseEnabled.Value; } } + + public static Uri GitHubEnterpriseUrl { get { return _gitHubEnterpriseUrl.Value; } } + + public static bool IsUsingToken + { + get + { + return !String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("OCTOKIT_GHE_OAUTHTOKEN")); + } + } + + public static string ClientId + { + get { return Environment.GetEnvironmentVariable("OCTOKIT_GHE_CLIENTID"); } + } + + public static string ClientSecret + { + get { return Environment.GetEnvironmentVariable("OCTOKIT_GHE_CLIENTSECRET"); } + } + + public static void DeleteRepo(Repository repository) + { + if (repository != null) + DeleteRepo(repository.Owner.Login, repository.Name); + } + + public static void DeleteRepo(string owner, string name) + { + var api = GetAuthenticatedClient(); + try + { + api.Repository.Delete(owner, name).Wait(TimeSpan.FromSeconds(15)); + } + catch { } + } + + public static IGitHubClient GetAuthenticatedClient() + { + return new GitHubClient(new ProductHeaderValue("OctokitEnterpriseTests"), GitHubEnterpriseUrl) + { + Credentials = Credentials + }; + } + + public static IGitHubClient GetBasicAuthClient() + { + return new GitHubClient(new ProductHeaderValue("OctokitEnterpriseTests"), GitHubEnterpriseUrl) + { + Credentials = BasicAuthCredentials + }; + } + + public static GitHubClient GetAuthenticatedApplicationClient() + { + return new GitHubClient(new ProductHeaderValue("OctokitEnterpriseTests"), GitHubEnterpriseUrl) + { + Credentials = ApplicationCredentials + }; + } + + public static IGitHubClient GetAnonymousClient() + { + return new GitHubClient(new ProductHeaderValue("OctokitEnterpriseTests"), GitHubEnterpriseUrl); + } + + public static IGitHubClient GetBadCredentialsClient() + { + return new GitHubClient(new ProductHeaderValue("OctokitEnterpriseTests"), GitHubEnterpriseUrl) + { + Credentials = new Credentials(Guid.NewGuid().ToString(), "bad-password") + }; + } + } +} diff --git a/Octokit.Tests.Integration/Helper.cs b/Octokit.Tests.Integration/Helper.cs index a2dd8065cb..efef287f69 100644 --- a/Octokit.Tests.Integration/Helper.cs +++ b/Octokit.Tests.Integration/Helper.cs @@ -50,9 +50,9 @@ public static class Helper return new Credentials(githubUsername, githubPassword); }); - static readonly Lazy _gitHubEnterpriseUrl = new Lazy(() => + static readonly Lazy _customUrl = new Lazy(() => { - string uri = Environment.GetEnvironmentVariable("OCTOKIT_GITHUBENTERPRISEURL"); + string uri = Environment.GetEnvironmentVariable("OCTOKIT_CUSTOMURL"); if (uri != null) return new Uri(uri); @@ -80,7 +80,9 @@ static Helper() public static Credentials BasicAuthCredentials { get { return _basicAuthCredentials.Value; } } - public static Uri GitHubEnterpriseUrl { get { return _gitHubEnterpriseUrl.Value; } } + public static Uri CustomUrl { get { return _customUrl.Value; } } + + public static Uri TargetUrl { get { return CustomUrl ?? GitHubClient.GitHubApiUrl; } } public static bool IsUsingToken { @@ -98,14 +100,6 @@ public static bool IsPaidAccount } } - public static bool IsGitHubEnterprise - { - get - { - return GitHubEnterpriseUrl != null; - } - } - public static string ClientId { get { return Environment.GetEnvironmentVariable("OCTOKIT_CLIENTID"); } @@ -151,7 +145,7 @@ public static Stream LoadFixture(string fileName) public static IGitHubClient GetAuthenticatedClient() { - return new GitHubClient(new ProductHeaderValue("OctokitTests"), GitHubEnterpriseUrl ?? GitHubClient.GitHubApiUrl) + return new GitHubClient(new ProductHeaderValue("OctokitTests"), TargetUrl) { Credentials = Credentials }; @@ -159,7 +153,7 @@ public static IGitHubClient GetAuthenticatedClient() public static IGitHubClient GetBasicAuthClient() { - return new GitHubClient(new ProductHeaderValue("OctokitTests"), GitHubEnterpriseUrl ?? GitHubClient.GitHubApiUrl) + return new GitHubClient(new ProductHeaderValue("OctokitTests"), TargetUrl) { Credentials = BasicAuthCredentials }; @@ -167,7 +161,7 @@ public static IGitHubClient GetBasicAuthClient() public static GitHubClient GetAuthenticatedApplicationClient() { - return new GitHubClient(new ProductHeaderValue("OctokitTests"), GitHubEnterpriseUrl ?? GitHubClient.GitHubApiUrl) + return new GitHubClient(new ProductHeaderValue("OctokitTests"), TargetUrl) { Credentials = ApplicationCredentials }; @@ -175,12 +169,12 @@ public static GitHubClient GetAuthenticatedApplicationClient() public static IGitHubClient GetAnonymousClient() { - return new GitHubClient(new ProductHeaderValue("OctokitTests"), GitHubEnterpriseUrl ?? GitHubClient.GitHubApiUrl); + return new GitHubClient(new ProductHeaderValue("OctokitTests"), TargetUrl); } public static IGitHubClient GetBadCredentialsClient() { - return new GitHubClient(new ProductHeaderValue("OctokitTests"), GitHubEnterpriseUrl ?? GitHubClient.GitHubApiUrl) + return new GitHubClient(new ProductHeaderValue("OctokitTests"), TargetUrl) { Credentials = new Credentials(Guid.NewGuid().ToString(), "bad-password") }; diff --git a/Octokit.Tests.Integration/Helpers/GitHubEnterpriseTestAttribute.cs b/Octokit.Tests.Integration/Helpers/GitHubEnterpriseTestAttribute.cs index 80c1df66c2..938bfb4641 100644 --- a/Octokit.Tests.Integration/Helpers/GitHubEnterpriseTestAttribute.cs +++ b/Octokit.Tests.Integration/Helpers/GitHubEnterpriseTestAttribute.cs @@ -20,7 +20,7 @@ public IEnumerable Discover(ITestFrameworkDiscoveryOptions disco if (Helper.Credentials == null) return Enumerable.Empty(); - if (!Helper.IsGitHubEnterprise) + if (!EnterpriseHelper.IsGitHubEnterpriseEnabled) return Enumerable.Empty(); return new[] { new XunitTestCase(diagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), testMethod) }; diff --git a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj index 3cbb946201..7781ebbcaf 100644 --- a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj +++ b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj @@ -77,7 +77,9 @@ + + @@ -106,6 +108,7 @@ + @@ -122,6 +125,8 @@ + + diff --git a/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseAdminStatsClientTests.cs b/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseAdminStatsClientTests.cs index 2cb61234c0..182ad393fd 100644 --- a/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseAdminStatsClientTests.cs +++ b/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseAdminStatsClientTests.cs @@ -11,7 +11,7 @@ public class ObservableEnterpriseAdminStatsClientTests public ObservableEnterpriseAdminStatsClientTests() { - _github = new ObservableGitHubClient(Helper.GetAuthenticatedClient()); + _github = new ObservableGitHubClient(EnterpriseHelper.GetAuthenticatedClient()); } [GitHubEnterpriseTest] diff --git a/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLicenseClientTests.cs b/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLicenseClientTests.cs new file mode 100644 index 0000000000..0c35fb5df5 --- /dev/null +++ b/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLicenseClientTests.cs @@ -0,0 +1,26 @@ +using System.Reactive.Linq; +using System.Threading.Tasks; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Integration +{ + public class ObservableEnterpriseLicenseClientTests + { + readonly IObservableGitHubClient _github; + + public ObservableEnterpriseLicenseClientTests() + { + _github = new ObservableGitHubClient(EnterpriseHelper.GetAuthenticatedClient()); + } + + [GitHubEnterpriseTest] + public async Task CanGet() + { + var observable = _github.Enterprise.License.Get(); + var licenseInfo = await observable; + + Assert.NotNull(licenseInfo); + } + } +} \ No newline at end of file diff --git a/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseOrganizationClientTests.cs b/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseOrganizationClientTests.cs new file mode 100644 index 0000000000..28997bada0 --- /dev/null +++ b/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseOrganizationClientTests.cs @@ -0,0 +1,36 @@ +using System; +using System.Reactive.Linq; +using System.Threading.Tasks; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Integration +{ + public class ObservableEnterpriseOrganizationClientTests + { + readonly IObservableGitHubClient _github; + + public ObservableEnterpriseOrganizationClientTests() + { + _github = new ObservableGitHubClient(EnterpriseHelper.GetAuthenticatedClient()); + } + + [GitHubEnterpriseTest] + public async Task CanCreateOrganization() + { + string orgLogin = Helper.MakeNameWithTimestamp("MyOrganization"); + string orgName = String.Concat(orgLogin, " Display Name"); + + var newOrganization = new NewOrganization(orgLogin, EnterpriseHelper.UserName, orgName); + var observable = _github.Enterprise.Organization.Create(newOrganization); + var organization = await observable; + + Assert.NotNull(organization); + + // Get organization and check login/name + var checkOrg = await _github.Organization.Get(orgLogin); + Assert.Equal(checkOrg.Login, orgLogin); + Assert.Equal(checkOrg.Name, orgName); + } + } +} \ No newline at end of file diff --git a/Octokit.Tests/Clients/Enterprise/EnterpriseLicenseClientTests.cs b/Octokit.Tests/Clients/Enterprise/EnterpriseLicenseClientTests.cs new file mode 100644 index 0000000000..947fa4a23b --- /dev/null +++ b/Octokit.Tests/Clients/Enterprise/EnterpriseLicenseClientTests.cs @@ -0,0 +1,23 @@ +using System; +using NSubstitute; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class EnterpriseLicenseClientTests + { + public class TheGetMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new EnterpriseLicenseClient(connection); + + string expectedUri = "enterprise/settings/license"; + client.Get(); + connection.Received().Get(Arg.Is(u => u.ToString() == expectedUri)); + } + } + } +} diff --git a/Octokit.Tests/Clients/Enterprise/EnterpriseOrganizationClientTests.cs b/Octokit.Tests/Clients/Enterprise/EnterpriseOrganizationClientTests.cs new file mode 100644 index 0000000000..4b078b1290 --- /dev/null +++ b/Octokit.Tests/Clients/Enterprise/EnterpriseOrganizationClientTests.cs @@ -0,0 +1,50 @@ +using System; +using System.Threading.Tasks; +using NSubstitute; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class EnterpriseOrganizationClientTests + { + public class TheCreateMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new EnterpriseOrganizationClient(connection); + + string expectedUri = "admin/organizations"; + client.Create(new NewOrganization("org", "admin", "org name")); + + connection.Received().Post(Arg.Is(u => u.ToString() == expectedUri), Arg.Any()); + } + + [Fact] + public void PassesRequestObject() + { + var connection = Substitute.For(); + var client = new EnterpriseOrganizationClient(connection); + + client.Create(new NewOrganization("org", "admin", "org name")); + + connection.Received().Post( + Arg.Any(), + Arg.Is(a => + a.Login == "org" + && a.Admin == "admin" + && a.ProfileName == "org name")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new EnterpriseOrganizationClient(connection); + + await Assert.ThrowsAsync(() => client.Create(null)); + } + } + } +} diff --git a/Octokit.Tests/Octokit.Tests.csproj b/Octokit.Tests/Octokit.Tests.csproj index 8df0ed6a85..a916d248ea 100644 --- a/Octokit.Tests/Octokit.Tests.csproj +++ b/Octokit.Tests/Octokit.Tests.csproj @@ -86,6 +86,8 @@ + + @@ -188,6 +190,8 @@ + + diff --git a/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLicenseClientTests.cs b/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLicenseClientTests.cs new file mode 100644 index 0000000000..b3f89bb5a4 --- /dev/null +++ b/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLicenseClientTests.cs @@ -0,0 +1,23 @@ +using System; +using NSubstitute; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests +{ + public class ObservableEnterpriseLicenseClientTests + { + public class TheGetMethod + { + [Fact] + public void CallsIntoClient() + { + var github = Substitute.For(); + var client = new ObservableEnterpriseLicenseClient(github); + + client.Get(); + github.Enterprise.License.Received(1).Get(); + } + } + } +} diff --git a/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseOrganizationClientTests.cs b/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseOrganizationClientTests.cs new file mode 100644 index 0000000000..47046e05fd --- /dev/null +++ b/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseOrganizationClientTests.cs @@ -0,0 +1,27 @@ +using System; +using NSubstitute; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests +{ + public class ObservableEnterpriseOrganizationClientTests + { + public class TheCreateMethod + { + [Fact] + public void CallsIntoClient() + { + var github = Substitute.For(); + var client = new ObservableEnterpriseOrganizationClient(github); + + client.Create(new NewOrganization("org", "admin", "org name")); + github.Enterprise.Organization.Received(1).Create( + Arg.Is(a => + a.Login == "org" + && a.Admin == "admin" + && a.ProfileName == "org name")); + } + } + } +} diff --git a/Octokit/Clients/Enterprise/EnterpriseClient.cs b/Octokit/Clients/Enterprise/EnterpriseClient.cs index 42e1763a64..e0109d1556 100644 --- a/Octokit/Clients/Enterprise/EnterpriseClient.cs +++ b/Octokit/Clients/Enterprise/EnterpriseClient.cs @@ -15,6 +15,8 @@ public class EnterpriseClient : ApiClient, IEnterpriseClient public EnterpriseClient(IApiConnection apiConnection) : base(apiConnection) { AdminStats = new EnterpriseAdminStatsClient(apiConnection); + License = new EnterpriseLicenseClient(apiConnection); + Organization = new EnterpriseOrganizationClient(apiConnection); } /// @@ -24,5 +26,21 @@ public EnterpriseClient(IApiConnection apiConnection) : base(apiConnection) /// See the Enterprise Admin Stats API documentation for more information. /// public IEnterpriseAdminStatsClient AdminStats { get; private set; } + + /// + /// A client for GitHub's Enterprise License API + /// + /// + /// See the Enterprise License API documentation for more information. + /// + public IEnterpriseLicenseClient License { get; private set; } + + /// + /// A client for GitHub's Enterprise Organization API + /// + /// + /// See the Enterprise Organization API documentation for more information. + /// + public IEnterpriseOrganizationClient Organization { get; private set; } } } diff --git a/Octokit/Clients/Enterprise/EnterpriseLicenseClient.cs b/Octokit/Clients/Enterprise/EnterpriseLicenseClient.cs new file mode 100644 index 0000000000..466388831d --- /dev/null +++ b/Octokit/Clients/Enterprise/EnterpriseLicenseClient.cs @@ -0,0 +1,32 @@ +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Enterprise License API + /// + /// + /// See the Enterprise License API documentation for more information. + /// + public class EnterpriseLicenseClient : ApiClient, IEnterpriseLicenseClient + { + public EnterpriseLicenseClient(IApiConnection apiConnection) + : base(apiConnection) + { } + + /// + /// Gets GitHub Enterprise License Information (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/license/#get-license-information + /// + /// The statistics. + public async Task Get() + { + var endpoint = ApiUrls.EnterpriseLicense(); + + return await ApiConnection.Get(endpoint) + .ConfigureAwait(false); + } + } +} diff --git a/Octokit/Clients/Enterprise/EnterpriseOrganizationClient.cs b/Octokit/Clients/Enterprise/EnterpriseOrganizationClient.cs new file mode 100644 index 0000000000..0848ff8ea7 --- /dev/null +++ b/Octokit/Clients/Enterprise/EnterpriseOrganizationClient.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Enterprise Organization API + /// + /// + /// See the Enterprise Organization API documentation for more information. + /// + public class EnterpriseOrganizationClient : ApiClient, IEnterpriseOrganizationClient + { + public EnterpriseOrganizationClient(IApiConnection apiConnection) + : base(apiConnection) + { } + + /// + /// Creates an Organization on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/orgs/#create-an-organization + /// + /// A instance describing the organization to be created + /// The created. + public async Task Create(NewOrganization newOrganization) + { + Ensure.ArgumentNotNull(newOrganization, "newOrganization"); + + var endpoint = ApiUrls.EnterpriseOrganization(); + + return await ApiConnection.Post(endpoint, newOrganization); + } + } +} diff --git a/Octokit/Clients/Enterprise/IEnterpriseClient.cs b/Octokit/Clients/Enterprise/IEnterpriseClient.cs index e5eff83a41..f1c6209fc3 100644 --- a/Octokit/Clients/Enterprise/IEnterpriseClient.cs +++ b/Octokit/Clients/Enterprise/IEnterpriseClient.cs @@ -15,5 +15,21 @@ public interface IEnterpriseClient /// See the Enterprise Admin Stats API documentation for more information. /// IEnterpriseAdminStatsClient AdminStats { get; } + + /// + /// A client for GitHub's Enterprise License API + /// + /// + /// See the Enterprise License API documentation for more information. + /// + IEnterpriseLicenseClient License { get; } + + /// + /// A client for GitHub's Enterprise Organization API + /// + /// + /// See the Enterprise Organization API documentation for more information. + /// + IEnterpriseOrganizationClient Organization { get; } } } diff --git a/Octokit/Clients/Enterprise/IEnterpriseLicenseClient.cs b/Octokit/Clients/Enterprise/IEnterpriseLicenseClient.cs new file mode 100644 index 0000000000..6e067e6551 --- /dev/null +++ b/Octokit/Clients/Enterprise/IEnterpriseLicenseClient.cs @@ -0,0 +1,24 @@ +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Enterprise License API + /// + /// + /// See the Enterprise License API documentation for more information. + /// + public interface IEnterpriseLicenseClient + { + /// + /// Gets GitHub Enterprise License Information (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/license/#get-license-information + /// + /// The statistics. + [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get")] + Task Get(); + } +} diff --git a/Octokit/Clients/Enterprise/IEnterpriseOrganizationClient.cs b/Octokit/Clients/Enterprise/IEnterpriseOrganizationClient.cs new file mode 100644 index 0000000000..4904480ce0 --- /dev/null +++ b/Octokit/Clients/Enterprise/IEnterpriseOrganizationClient.cs @@ -0,0 +1,23 @@ +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Enterprise Organization API + /// + /// + /// See the Enterprise Organization API documentation for more information. + /// + public interface IEnterpriseOrganizationClient + { + /// + /// Creates an Organization on a GitHub Enterprise appliance (must be Site Admin user). + /// + /// + /// https://developer.github.com/v3/enterprise/orgs/#create-an-organization + /// + /// A instance describing the organization to be created + /// The created. + Task Create(NewOrganization newOrganization); + } +} diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index 229c1970a2..974dba11fc 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -1664,6 +1664,16 @@ public static Uri EnterpriseAdminStatsAll() return EnterpriseAdminStats("all"); } + public static Uri EnterpriseLicense() + { + return "enterprise/settings/license".FormatUri(); + } + + public static Uri EnterpriseOrganization() + { + return "admin/organizations".FormatUri(); + } + /// /// Creates the relative for altering administration status of a user. /// diff --git a/Octokit/Models/Request/NewOrganization.cs b/Octokit/Models/Request/NewOrganization.cs new file mode 100644 index 0000000000..2a8a4a61ef --- /dev/null +++ b/Octokit/Models/Request/NewOrganization.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + /// + /// Describes a new organization to create via the method. + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class NewOrganization + { + /// + /// Initializes a new instance of the class. + /// + /// The organization's username + /// The login of the user who will manage this organization + public NewOrganization(string login, string admin) : this(login, admin, null) + { } + + /// + /// Initializes a new instance of the class. + /// + /// The organization's username + /// The login of the user who will manage this organization + /// The organization's display name + public NewOrganization(string login, string admin, string profileName) + { + Login = login; + Admin = admin; + ProfileName = profileName; + } + + /// + /// The organization's username (required) + /// + public string Login { get; private set; } + + /// + /// The login of the user who will manage this organization (required) + /// + public string Admin { get; private set; } + + /// + /// The organization's display name + /// + public string ProfileName { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Login: {0} Admin: {1} ProfileName: {2}", Login, Admin, ProfileName ?? ""); + } + } + } +} diff --git a/Octokit/Models/Response/Enterprise/LicenseInfo.cs b/Octokit/Models/Response/Enterprise/LicenseInfo.cs new file mode 100644 index 0000000000..03bd174c04 --- /dev/null +++ b/Octokit/Models/Response/Enterprise/LicenseInfo.cs @@ -0,0 +1,66 @@ +using System; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class LicenseInfo + { + public LicenseInfo() { } + + public LicenseInfo(int seats, int seatsUsed, int seatsAvailable, string kind, int daysUntilExpiration, DateTimeOffset expireAt) + { + Seats = seats; + SeatsUsed = seatsUsed; + SeatsAvailable = seatsAvailable; + Kind = kind; + DaysUntilExpiration = daysUntilExpiration; + ExpireAt = expireAt; + } + + public int Seats + { + get; + private set; + } + + public int SeatsUsed + { + get; + private set; + } + + public int SeatsAvailable + { + get; + private set; + } + + public string Kind + { + get; + private set; + } + + public int DaysUntilExpiration + { + get; + private set; + } + + public DateTimeOffset ExpireAt + { + get; + private set; + } + + internal string DebuggerDisplay + { + get + { + return String.Format(CultureInfo.InvariantCulture, "Seats: {0} SeatsUsed: {1} DaysUntilExpiration: {2}", Seats, SeatsUsed, DaysUntilExpiration); + } + } + } +} \ No newline at end of file diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index 08bf819f80..7611c68317 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -437,6 +437,12 @@ + + + + + + \ No newline at end of file diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index 157b5ad22c..d865a56ae1 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -442,6 +442,12 @@ + + + + + + diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index d0926bc589..0788df85b4 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -438,6 +438,12 @@ + + + + + + diff --git a/Octokit/Octokit-Portable.csproj b/Octokit/Octokit-Portable.csproj index b110b9f94c..974475ebf5 100644 --- a/Octokit/Octokit-Portable.csproj +++ b/Octokit/Octokit-Portable.csproj @@ -434,6 +434,12 @@ + + + + + + diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index 0b60e716ef..359b023b97 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -441,6 +441,12 @@ + + + + + + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index 253a567f80..10e79fc7e4 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -59,7 +59,11 @@ + + + + @@ -106,6 +110,7 @@ + @@ -142,6 +147,7 @@ + diff --git a/script/configure-integration-tests.ps1 b/script/configure-integration-tests.ps1 index 5592c3e495..590291d95c 100644 --- a/script/configure-integration-tests.ps1 +++ b/script/configure-integration-tests.ps1 @@ -19,6 +19,8 @@ function AskYesNoQuestion([string]$question, [string]$key) } Write-Host + + return ($answer -eq "Y") } function VerifyEnvironmentVariable([string]$friendlyName, [string]$key, [bool]$optional = $false) @@ -71,11 +73,26 @@ VerifyEnvironmentVariable "account name" "OCTOKIT_GITHUBUSERNAME" VerifyEnvironmentVariable "account password" "OCTOKIT_GITHUBPASSWORD" $true VerifyEnvironmentVariable "OAuth token" "OCTOKIT_OAUTHTOKEN" -AskYesNoQuestion "Do you have private repositories associated with your test account?" "OCTOKIT_PRIVATEREPOSITORIES" +AskYesNoQuestion "Do you have private repositories associated with your test account?" "OCTOKIT_PRIVATEREPOSITORIES" | Out-Null VerifyEnvironmentVariable "organization name" "OCTOKIT_GITHUBORGANIZATION" $true -VerifyEnvironmentVariable "GitHub Enterprise Server URL" "OCTOKIT_GITHUBENTERPRISEURL" $true +VerifyEnvironmentVariable "Override GitHub URL" "OCTOKIT_CUSTOMURL" $true VerifyEnvironmentVariable "application ClientID" "OCTOKIT_CLIENTID" $true -VerifyEnvironmentVariable "application Secret" "OCTOKIT_CLIENTSECRET" $true \ No newline at end of file +VerifyEnvironmentVariable "application Secret" "OCTOKIT_CLIENTSECRET" $true + + +if (AskYesNoQuestion "Do you wish to enable GitHub Enterprise (GHE) Integration Tests?" "OCTOKIT_GHE_ENABLED") +{ + VerifyEnvironmentVariable "GitHub Enterprise account name" "OCTOKIT_GHE_USERNAME" + VerifyEnvironmentVariable "GitHub Enterprise account password" "OCTOKIT_GHE_PASSWORD" $true + VerifyEnvironmentVariable "GitHub Enterprise OAuth token" "OCTOKIT_GHE_OAUTHTOKEN" + + VerifyEnvironmentVariable "GitHub Enterprise organization name" "OCTOKIT_GHE_ORGANIZATION" $true + + VerifyEnvironmentVariable "GitHub Enterprise URL" "OCTOKIT_GHE_URL" $true + + VerifyEnvironmentVariable "GitHub Enterprise application ClientID" "OCTOKIT_GHE_CLIENTID" $true + VerifyEnvironmentVariable "GitHub Enterprise application Secret" "OCTOKIT_GHE_CLIENTSECRET" $true +} \ No newline at end of file