add support for organization web hooks (#1884)

This commit is contained in:
Martin Alex Philip Dawson
2020-06-07 20:53:20 +01:00
committed by GitHub
parent 62c0b1fe08
commit 8d3e7b3c2c
23 changed files with 1726 additions and 16 deletions

View File

@@ -0,0 +1,65 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reactive;
namespace Octokit.Reactive
{
public interface IObservableOrganizationHooksClient
{
/// <summary>
/// Gets the list of hooks defined for a organization
/// </summary>
/// <param name="org">The organizations name</param>
/// <remarks>See <a href="http://developer.github.com/v3/orgs/hooks/#list-hooks">API documentation</a> for more information.</remarks>
IObservable<OrganizationHook> GetAll(string org);
/// <summary>
/// Gets the list of hooks defined for a organization
/// </summary>
/// <param name="org">The organizations name</param>
/// <param name="options">Options for changing the API response</param>
/// <remarks>See <a href="http://developer.github.com/v3/orgs/hooks/#list-hooks">API documentation</a> for more information.</remarks>
IObservable<OrganizationHook> GetAll(string org, ApiOptions options);
/// <summary>
/// Gets a single hook defined for a organization by id
/// </summary>
/// <param name="org">The organizations name</param>
/// <param name="hookId">The organizations hook id</param>
/// <remarks>See <a href="https://developer.github.com/v3/orgs/hooks/#get-single-hook">API documentation</a> for more information.</remarks>
[SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get", Justification = "This is ok; we're matching HTTP verbs not keyworks")]
IObservable<OrganizationHook> Get(string org, int hookId);
/// <summary>
/// Creates a hook for a organization
/// </summary>
/// <remarks>See <a href="https://developer.github.com/v3/orgs/hooks/#create-a-hook">API documentation</a> for more information.</remarks>
/// <returns></returns>
IObservable<OrganizationHook> Create(string org, NewOrganizationHook hook);
/// <summary>
/// Edits a hook for a organization
/// </summary>
/// <param name="org">The organizations name</param>
/// <param name="hookId">The organizations hook id</param>
/// <param name="hook">The hook's parameters</param>
/// <remarks>See <a href="https://developer.github.com/v3/orgs/hooks/#edit-a-hook">API documentation</a> for more information.</remarks>
IObservable<OrganizationHook> Edit(string org, int hookId, EditOrganizationHook hook);
/// <summary>
/// This will trigger a ping event to be sent to the hook.
/// </summary>
/// <param name="org">The organizations name</param>
/// <param name="hookId">The organizations hook id</param>
/// <remarks>See <a href="https://developer.github.com/v3/orgs/hooks/#ping-a-hook">API documentation</a> for more information.</remarks>
IObservable<Unit> Ping(string org, int hookId);
/// <summary>
/// Deletes a hook for a organization
/// </summary>
/// <param name="org">The organizations name</param>
/// <param name="hookId">The organizations hook id</param>
/// <remarks>See <a href="https://developer.github.com/v3/orgs/hooks/#delete-a-hook">API documentation</a> for more information.</remarks>
IObservable<Unit> Delete(string org, int hookId);
}
}

View File

@@ -15,6 +15,12 @@ namespace Octokit.Reactive
/// </summary>
IObservableTeamsClient Team { get; }
/// <summary>
/// A client for GitHub's Organization Hooks API.
/// </summary>
/// <remarks>See <a href="http://developer.github.com/v3/orgs/hooks/">Hooks API documentation</a> for more information.</remarks>
IObservableOrganizationHooksClient Hook { get; }
/// <summary>
/// Returns a client to manage outside collaborators of an organization.
/// </summary>
@@ -77,10 +83,10 @@ namespace Octokit.Reactive
/// <summary>
/// Update the specified organization with data from <see cref="OrganizationUpdate"/>.
/// </summary>
/// <param name="organizationName">The name of the organization to update.</param>
/// <param name="org">The name of the organization to update.</param>
/// <param name="updateRequest"></param>
/// <exception cref="AuthorizationException">Thrown if the client is not authenticated.</exception>
/// <returns>A <see cref="Organization"/></returns>
IObservable<Organization> Update(string organizationName, OrganizationUpdate updateRequest);
IObservable<Organization> Update(string org, OrganizationUpdate updateRequest);
}
}

View File

@@ -0,0 +1,116 @@
using System;
using System.Reactive;
using System.Reactive.Threading.Tasks;
using Octokit.Reactive.Internal;
namespace Octokit.Reactive
{
public class ObservableOrganizationHooksClient : IObservableOrganizationHooksClient
{
readonly IOrganizationHooksClient _client;
readonly IConnection _connection;
public ObservableOrganizationHooksClient(IGitHubClient client)
{
Ensure.ArgumentNotNull(client, nameof(client));
_client = client.Organization.Hook;
_connection = client.Connection;
}
/// <summary>
/// Gets the list of hooks defined for a organization
/// </summary>
/// <param name="org">The organizations name</param>
/// <remarks>See <a href="http://developer.github.com/v3/orgs/hooks/#list-hooks">API documentation</a> for more information.</remarks>
public IObservable<OrganizationHook> GetAll(string org)
{
Ensure.ArgumentNotNullOrEmptyString(org, nameof(org));
return _connection.GetAndFlattenAllPages<OrganizationHook>(ApiUrls.OrganizationHooks(org));
}
/// <summary>
/// Gets the list of hooks defined for a organization
/// </summary>
/// <param name="org">The organizations name</param>
/// <param name="options">Options for changing the API response</param>
/// <remarks>See <a href="http://developer.github.com/v3/orgs/hooks/#list-hooks">API documentation</a> for more information.</remarks>
public IObservable<OrganizationHook> GetAll(string org, ApiOptions options)
{
Ensure.ArgumentNotNullOrEmptyString(org, nameof(org));
Ensure.ArgumentNotNull(options, nameof(options));
return _connection.GetAndFlattenAllPages<OrganizationHook>(ApiUrls.OrganizationHooks(org), options);
}
/// <summary>
/// Gets a single hook defined for a organization by id
/// </summary>
/// <param name="org">The organizations name</param>
/// <param name="hookId">The organizations hook id</param>
/// <remarks>See <a href="http://developer.github.com/v3/orgs/hooks/#get-single-hook">API documentation</a> for more information.</remarks>
public IObservable<OrganizationHook> Get(string org, int hookId)
{
Ensure.ArgumentNotNullOrEmptyString(org, nameof(org));
return _client.Get(org, hookId).ToObservable();
}
/// <summary>
/// Creates a hook for a organization
/// </summary>
/// <param name="org">The organizations name</param>
/// <param name="hook">The hook's parameters</param>
/// <remarks>See <a href="http://developer.github.com/v3/orgs/hooks/#create-a-hook">API documentation</a> for more information.</remarks>
public IObservable<OrganizationHook> Create(string org, NewOrganizationHook hook)
{
Ensure.ArgumentNotNullOrEmptyString(org, nameof(org));
Ensure.ArgumentNotNull(hook, nameof(hook));
return _client.Create(org, hook).ToObservable();
}
/// <summary>
/// Edits a hook for a organization
/// </summary>
/// <param name="org">The organizations name</param>
/// <param name="hookId">The organizations hook id</param>
/// <param name="hook">The hook's parameters</param>
/// <remarks>See <a href="http://developer.github.com/v3/orgs/hooks/#edit-a-hook">API documentation</a> for more information.</remarks>
/// <returns></returns>
public IObservable<OrganizationHook> Edit(string org, int hookId, EditOrganizationHook hook)
{
Ensure.ArgumentNotNullOrEmptyString(org, nameof(org));
Ensure.ArgumentNotNull(hook, nameof(hook));
return _client.Edit(org, hookId, hook).ToObservable();
}
/// <summary>
/// This will trigger a ping event to be sent to the hook.
/// </summary>
/// <param name="org">The organizations name</param>
/// <param name="hookId">The organizations hook id</param>
/// <remarks>See <a href="http://developer.github.com/v3/orgs/hooks/#ping-a-hook">API documentation</a> for more information.</remarks>
public IObservable<Unit> Ping(string org, int hookId)
{
Ensure.ArgumentNotNullOrEmptyString(org, nameof(org));
return _client.Ping(org, hookId).ToObservable();
}
/// <summary>
/// Deletes a hook for a organization
/// </summary>
/// <param name="org">The organizations name</param>
/// <param name="hookId">The organizations hook id</param>
/// <remarks>See <a href="http://developer.github.com/v3/orgs/hooks/#delete-a-hook">API documentation</a> for more information.</remarks>
public IObservable<Unit> Delete(string org, int hookId)
{
Ensure.ArgumentNotNullOrEmptyString(org, nameof(org));
return _client.Delete(org, hookId).ToObservable();
}
}
}

View File

@@ -19,6 +19,7 @@ namespace Octokit.Reactive
Member = new ObservableOrganizationMembersClient(client);
Team = new ObservableTeamsClient(client);
Hook = new ObservableOrganizationHooksClient(client);
OutsideCollaborator = new ObservableOrganizationOutsideCollaboratorsClient(client);
_client = client.Organization;
@@ -35,6 +36,12 @@ namespace Octokit.Reactive
/// </summary>
public IObservableTeamsClient Team { get; private set; }
/// <summary>
/// A client for GitHub's Organization Hooks API.
/// </summary>
/// <remarks>See <a href="http://developer.github.com/v3/orgs/hooks/">Hooks API documentation</a> for more information.</remarks>
public IObservableOrganizationHooksClient Hook { get; private set; }
/// <summary>
/// Returns a client to manage outside collaborators of an organization.
/// </summary>
@@ -125,16 +132,17 @@ namespace Octokit.Reactive
/// <summary>
/// Update the specified organization with data from <see cref="OrganizationUpdate"/>.
/// </summary>
/// <param name="organizationName">The name of the organization to update.</param>
/// <param name="org">The name of the organization to update.</param>
/// <param name="updateRequest"></param>
/// <exception cref="AuthorizationException">Thrown if the client is not authenticated.</exception>
/// <returns>A <see cref="Organization"/></returns>
public IObservable<Organization> Update(string organizationName, OrganizationUpdate updateRequest)
public IObservable<Organization> Update(string org, OrganizationUpdate updateRequest)
{
Ensure.ArgumentNotNullOrEmptyString(organizationName, nameof(organizationName));
Ensure.ArgumentNotNullOrEmptyString(org, nameof(org));
Ensure.ArgumentNotNull(updateRequest, nameof(updateRequest));
return _client.Update(organizationName, updateRequest).ToObservable();
return _client.Update(org, updateRequest).ToObservable();
}
}
}

View File

@@ -0,0 +1,255 @@
using Octokit.Tests.Integration.Fixtures;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
namespace Octokit.Tests.Integration.Clients
{
public class OrganizationHooksClientTests
{
[Collection(OrganizationsHooksCollection.Name)]
public class TheGetAllMethod
{
readonly OrganizationsHooksFixture _fixture;
public TheGetAllMethod(OrganizationsHooksFixture fixture)
{
_fixture = fixture;
}
[IntegrationTest]
public async Task ReturnsAllHooksFromOrganization()
{
var github = Helper.GetAuthenticatedClient();
var hooks = await github.Organization.Hook.GetAll( _fixture.org);
Assert.Equal(_fixture.ExpectedHooks.Count, hooks.Count);
var actualHook = hooks[0];
AssertHook(_fixture.ExpectedHook, actualHook);
}
[IntegrationTest]
public async Task ReturnsCorrectCountOfHooksWithoutStart()
{
var github = Helper.GetAuthenticatedClient();
var options = new ApiOptions
{
PageSize = 5,
PageCount = 1
};
var hooks = await github.Organization.Hook.GetAll(_fixture.org, options);
Assert.Equal(_fixture.ExpectedHooks.Count, hooks.Count);
}
[IntegrationTest]
public async Task ReturnsCorrectCountOfHooksWithStart()
{
var github = Helper.GetAuthenticatedClient();
var options = new ApiOptions
{
PageSize = 3,
PageCount = 1,
StartPage = 2
};
var hooks = await github.Organization.Hook.GetAll(_fixture.org, options);
Assert.Equal(1, hooks.Count);
}
[IntegrationTest]
public async Task ReturnsDistinctResultsBasedOnStartPage()
{
var github = Helper.GetAuthenticatedClient();
var startOptions = new ApiOptions
{
PageSize = 2,
PageCount = 1
};
var firstPage = await github.Organization.Hook.GetAll(_fixture.org, startOptions);
var skipStartOptions = new ApiOptions
{
PageSize = 2,
PageCount = 1,
StartPage = 2
};
var secondPage = await github.Organization.Hook.GetAll(_fixture.org, skipStartOptions);
Assert.NotEqual(firstPage[0].Id, secondPage[0].Id);
Assert.NotEqual(firstPage[1].Id, secondPage[1].Id);
}
}
[Collection(OrganizationsHooksCollection.Name)]
public class TheGetMethod
{
readonly OrganizationsHooksFixture _fixture;
public TheGetMethod(OrganizationsHooksFixture fixture)
{
_fixture = fixture;
}
[IntegrationTest]
public async Task GetHookByCreatedId()
{
var github = Helper.GetAuthenticatedClient();
var actualHook = await github.Organization.Hook.Get(_fixture.org, _fixture.ExpectedHook.Id);
AssertHook(_fixture.ExpectedHook, actualHook);
}
}
[Collection(OrganizationsHooksCollection.Name)]
public class TheCreateMethod
{
readonly OrganizationsHooksFixture _fixture;
public TheCreateMethod(OrganizationsHooksFixture fixture)
{
_fixture = fixture;
}
[IntegrationTest]
public async Task CreateAWebHookForTestOrganization()
{
var github = Helper.GetAuthenticatedClient();
var url = "http://test.com/example";
var contentType = OrgWebHookContentType.Json;
//var secret = "53cr37";
var config = new Dictionary<string, string>
{
{ "url", "http://hostname.url" },
{ "content_type", "json" }
};
var parameters = new NewOrganizationHook("web", config)
{
Events = new[] { "push" },
Active = false
};
var hook = await github.Organization.Hook.Create(_fixture.org, parameters.ToRequest());
var baseHookUrl = CreateExpectedBaseHookUrl(_fixture.org, hook.Id);
var webHookConfig = CreateExpectedConfigDictionary(config, url, contentType);
Assert.Equal("web", hook.Name);
Assert.Equal(new[] { "push" }.ToList(), hook.Events.ToList());
Assert.Equal(baseHookUrl, hook.Url);
Assert.Equal(baseHookUrl + "/pings", hook.PingUrl);
Assert.NotNull(hook.CreatedAt);
Assert.NotNull(hook.UpdatedAt);
Assert.Equal(webHookConfig.Keys, hook.Config.Keys);
//Assert.Equal(webHookConfig.Values, hook.Config.Values);
Assert.Equal(false, hook.Active);
}
Dictionary<string, string> CreateExpectedConfigDictionary(Dictionary<string, string> config, string url, OrgWebHookContentType contentType)
{
return new Dictionary<string, string>
{
}.Union(config).ToDictionary(k => k.Key, v => v.Value);
}
string CreateExpectedBaseHookUrl(string org, int id)
{
return "https://api.github.com/orgs/" + org+ "/hooks/" + id;
}
}
[Collection(OrganizationsHooksCollection.Name)]
public class TheEditMethod
{
readonly OrganizationsHooksFixture _fixture;
public TheEditMethod(OrganizationsHooksFixture fixture)
{
_fixture = fixture;
}
[IntegrationTest]
public async Task EditHookTest()
{
var github = Helper.GetAuthenticatedClient();
var editOrganizationHook = new EditOrganizationHook
{
Events = new[] { "pull_request" }
};
var actualHook = await github.Organization.Hook.Edit( _fixture.org, _fixture.ExpectedHook.Id, editOrganizationHook);
var expectedConfig = new Dictionary<string, string> { { "content_type", "json" }, { "url", "http://test.com/example" } };
Assert.Equal(new[] { "commit_comment", "pull_request" }.ToList(), actualHook.Events.ToList());
Assert.Equal(expectedConfig.Keys, actualHook.Config.Keys);
Assert.Equal(expectedConfig.Values, actualHook.Config.Values);
}
}
[Collection(OrganizationsHooksCollection.Name)]
public class ThePingMethod
{
readonly OrganizationsHooksFixture _fixture;
public ThePingMethod(OrganizationsHooksFixture fixture)
{
_fixture = fixture;
}
[IntegrationTest]
public async Task PingACreatedHook()
{
var github = Helper.GetAuthenticatedClient();
await github.Organization.Hook.Ping( _fixture.org, _fixture.ExpectedHook.Id);
}
}
[Collection(OrganizationsHooksCollection.Name)]
public class TheDeleteMethod
{
readonly OrganizationsHooksFixture _fixture;
public TheDeleteMethod(OrganizationsHooksFixture fixture)
{
_fixture = fixture;
}
[IntegrationTest]
public async Task DeleteCreatedWebHook()
{
var github = Helper.GetAuthenticatedClient();
await github.Organization.Hook.Delete(_fixture.org, _fixture.ExpectedHook.Id);
var hooks = await github.Organization.Hook.GetAll( _fixture.org);
Assert.Empty(hooks);
}
}
static void AssertHook(OrganizationHook expectedHook, OrganizationHook actualHook)
{
Assert.Equal(expectedHook.Id, actualHook.Id);
Assert.Equal(expectedHook.Active, actualHook.Active);
Assert.Equal(expectedHook.Config, actualHook.Config);
Assert.Equal(expectedHook.CreatedAt, actualHook.CreatedAt);
Assert.Equal(expectedHook.Name, actualHook.Name);
Assert.Equal(expectedHook.PingUrl, actualHook.PingUrl);
Assert.Equal(expectedHook.TestUrl, actualHook.TestUrl);
Assert.Equal(expectedHook.UpdatedAt, actualHook.UpdatedAt);
Assert.Equal(expectedHook.Url, actualHook.Url);
}
}
}

View File

@@ -1,4 +1,4 @@
using Octokit.Tests.Integration.fixtures;
using Octokit.Tests.Integration.Fixtures;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

View File

@@ -1,8 +1,8 @@
using System.Reactive.Linq;
using System.Threading.Tasks;
using Octokit.Reactive;
using Octokit.Tests.Integration.fixtures;
using Xunit;
using Octokit.Tests.Integration.Fixtures;
namespace Octokit.Tests.Integration.Reactive
{

View File

@@ -0,0 +1,10 @@
using Xunit;
namespace Octokit.Tests.Integration.Fixtures
{
[CollectionDefinition(Name)]
public class OrganizationsHooksCollection : ICollectionFixture<OrganizationsHooksFixture>
{
public const string Name = "Organization Hooks Collection";
}
}

View File

@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
namespace Octokit.Tests.Integration.Fixtures
{
public class OrganizationsHooksFixture : IDisposable
{
readonly IGitHubClient _github;
readonly OrganizationHook _hook;
readonly private string _organizationFixture;
readonly IList<OrganizationHook> _hooks;
public OrganizationsHooksFixture()
{
_github = Helper.GetAuthenticatedClient();
_organizationFixture = Helper.Organization;
_hooks = new List<OrganizationHook>(5)
{
CreateHook(_github, _organizationFixture, "awscodedeploy", "deployment"),
CreateHook(_github, _organizationFixture, "awsopsworks", "push"),
CreateHook(_github, _organizationFixture, "activecollab", "push"),
CreateHook(_github, _organizationFixture, "acunote", "push")
};
_hook = _hooks[0];
}
public string org { get { return _organizationFixture; } }
public OrganizationHook ExpectedHook { get { return _hook; } }
public IList<OrganizationHook> ExpectedHooks { get { return _hooks; } }
public void Dispose()
{
_github.Organization.Hook.Delete(_organizationFixture,_hook.Id);
}
static OrganizationHook CreateHook(IGitHubClient github, string orgFixture, string hookName, string eventName)
{
var config = new Dictionary<string, string> { { "content_type", "json" }, { "url", "http://test.com/example" } };
var parameters = new NewOrganizationHook(hookName, config)
{
Events = new[] { eventName },
Active = false
};
var createdHook = github.Organization.Hook.Create(orgFixture, parameters);
return createdHook.Result;
}
}
}

View File

@@ -1,6 +1,6 @@
using Xunit;
namespace Octokit.Tests.Integration.fixtures
namespace Octokit.Tests.Integration.Fixtures
{
[CollectionDefinition(Name)]
public class RepositoriesHooksCollection : ICollectionFixture<RepositoriesHooksFixture>

View File

@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
namespace Octokit.Tests.Integration.fixtures
namespace Octokit.Tests.Integration.Fixtures
{
public class RepositoriesHooksFixture : IDisposable
{

View File

@@ -0,0 +1,248 @@
using NSubstitute;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;
namespace Octokit.Tests.Clients
{
public class OrganizationHooksClientTests
{
public class TheCtor
{
[Fact]
public void EnsuresNonNullArguments()
{
Assert.Throws<ArgumentNullException>(
() => new OrganizationHooksClient(null));
}
}
public class TheGetAllMethod
{
[Fact]
public async Task RequestsCorrectUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new OrganizationsClient(connection);
await client.Hook.GetAll("org");
connection.Received().GetAll<OrganizationHook>(Arg.Is<Uri>(u => u.ToString() == "orgs/org/hooks"));
}
[Fact]
public async Task RequestsCorrectUrlWithApiOptions()
{
var connection = Substitute.For<IApiConnection>();
var client = new OrganizationsClient(connection);
var options = new ApiOptions
{
PageCount = 1,
PageSize = 1,
StartPage = 1
};
await client.Hook.GetAll("org", options);
connection.Received(1)
.GetAll<OrganizationHook>(Arg.Is<Uri>(u => u.ToString() == "orgs/org/hooks"),
options);
}
[Fact]
public async Task EnsuresNonNullArguments()
{
var client = new OrganizationsClient(Substitute.For<IApiConnection>());
await Assert.ThrowsAsync<ArgumentNullException>(() => client.Hook.GetAll(null));
}
[Fact]
public async Task EnsuresNonEmptyArguments()
{
var client = new OrganizationsClient(Substitute.For<IApiConnection>());
await Assert.ThrowsAsync<ArgumentException>(() => client.Hook.GetAll(""));
}
}
public class TheGetMethod
{
[Fact]
public async Task RequestsCorrectUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new OrganizationsClient(connection);
await client.Hook.Get("org", 12345678);
connection.Received().Get<OrganizationHook>(Arg.Is<Uri>(u => u.ToString() == "orgs/org/hooks/12345678"));
}
[Fact]
public async Task EnsuresNonNullArguments()
{
var client = new OrganizationsClient(Substitute.For<IApiConnection>());
await Assert.ThrowsAsync<ArgumentNullException>(() => client.Hook.Get(null, 123));
}
[Fact]
public async Task EnsuresNonEmptyArguments()
{
var client = new OrganizationsClient(Substitute.For<IApiConnection>());
await Assert.ThrowsAsync<ArgumentException>(() => client.Hook.Get("",123));
}
}
public class TheCreateMethod
{
[Fact]
public void RequestsCorrectUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new OrganizationsClient(connection);
var hook = new NewOrganizationHook("name", new Dictionary<string, string> { { "config", "" } });
client.Hook.Create("org", hook);
connection.Received().Post<OrganizationHook>(Arg.Is<Uri>(u => u.ToString() == "orgs/org/hooks"), hook);
}
[Fact]
public async Task EnsuresNonNullArguments()
{
var client = new OrganizationsClient(Substitute.For<IApiConnection>());
var config = new Dictionary<string, string> { { "config", "" } };
await Assert.ThrowsAsync<ArgumentNullException>(() => client.Hook.Create(null, new NewOrganizationHook("name", config)));
await Assert.ThrowsAsync<ArgumentNullException>(() => client.Hook.Create("name", null));
}
[Fact]
public void EnsuresNonEmptyArguments()
{
var client = new OrganizationsClient(Substitute.For<IApiConnection>());
var config = new Dictionary<string, string> { { "url", "" } };
Assert.ThrowsAsync<ArgumentException>(() => client.Hook.Create("", new NewOrganizationHook("name", config)));
}
[Fact]
public void UsesTheSuppliedHook()
{
var connection = Substitute.For<IApiConnection>();
var client = new OrganizationsClient(connection);
var newOrganizationHook = new NewOrganizationHook("name", new Dictionary<string, string> { { "config", "" } });
client.Hook.Create("org", newOrganizationHook);
connection.Received().Post<OrganizationHook>(Arg.Any<Uri>(), newOrganizationHook);
}
}
public class TheEditMethod
{
[Fact]
public void RequestsCorrectUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new OrganizationsClient(connection);
var hook = new EditOrganizationHook();
client.Hook.Edit("org", 12345678, hook);
connection.Received().Patch<OrganizationHook>(Arg.Is<Uri>(u => u.ToString() == "orgs/org/hooks/12345678"), hook);
}
[Fact]
public async Task EnsuresNonNullArguments()
{
var client = new OrganizationsClient(Substitute.For<IApiConnection>());
await Assert.ThrowsAsync<ArgumentNullException>(() => client.Hook.Edit( null, 12345678, new EditOrganizationHook()));
await Assert.ThrowsAsync<ArgumentNullException>(() => client.Hook.Edit( "name", 12345678, null));
}
[Fact]
public void EnsuresNonEmptyArguments()
{
var client = new OrganizationsClient(Substitute.For<IApiConnection>());
Assert.ThrowsAsync<ArgumentException>(() => client.Hook.Edit("", 123, new EditOrganizationHook()));
}
[Fact]
public void UsesTheSuppliedHook()
{
var connection = Substitute.For<IApiConnection>();
var client = new OrganizationsClient(connection);
var editOrganizationHook = new EditOrganizationHook() { Active = false };
client.Hook.Edit("org", 12345678, editOrganizationHook);
connection.Received().Patch<OrganizationHook>(Arg.Any<Uri>(), editOrganizationHook);
}
}
public class ThePingMethod
{
[Fact]
public async Task EnsuresNonNullArguments()
{
var client = new OrganizationsClient(Substitute.For<IApiConnection>());
await Assert.ThrowsAsync<ArgumentNullException>(() => client.Hook.Ping(null, 12345678));
}
[Fact]
public void EnsuresNonEmptyArguments()
{
var client = new OrganizationsClient(Substitute.For<IApiConnection>());
Assert.ThrowsAsync<ArgumentException>(() => client.Hook.Ping("", 123));
}
[Fact]
public void RequestsCorrectUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new OrganizationsClient(connection);
client.Hook.Ping("org", 12345678);
connection.Received().Post(Arg.Is<Uri>(u => u.ToString() == "orgs/org/hooks/12345678/pings"));
}
}
public class TheDeleteMethod
{
[Fact]
public void RequestsCorrectUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new OrganizationsClient(connection);
client.Hook.Delete("org", 12345678);
connection.Received().Delete(Arg.Is<Uri>(u => u.ToString() == "orgs/org/hooks/12345678"));
}
[Fact]
public async Task EnsuresNonNullArguments()
{
var client = new OrganizationsClient(Substitute.For<IApiConnection>());
await Assert.ThrowsAsync<ArgumentNullException>(() => client.Hook.Delete(null, 12345678));
}
[Fact]
public void EnsuresNonEmptyArguments()
{
var client = new OrganizationsClient(Substitute.For<IApiConnection>());
Assert.ThrowsAsync<ArgumentException>(() => client.Hook.Delete("", 123));
}
}
}
}

View File

@@ -0,0 +1,157 @@
using System.Collections.Generic;
using Xunit;
namespace Octokit.Tests.Models
{
public class NewOrganizationWebHookTests
{
public class TheCtor
{
[Fact]
public void UsesDefaultValuesForDefaultConfig()
{
var create = new NewOrganizationWebHook("windowsazure", new Dictionary<string, string>(), "http://test.com/example");
Assert.Equal(create.Url, "http://test.com/example");
Assert.Equal(create.ContentType, OrgWebHookContentType.Form);
Assert.Empty(create.Secret);
Assert.False(create.InsecureSsl);
var request = create.ToRequest();
Assert.Equal(request.Config.Count, 4);
Assert.True(request.Config.ContainsKey("url"));
Assert.True(request.Config.ContainsKey("content_type"));
Assert.True(request.Config.ContainsKey("secret"));
Assert.True(request.Config.ContainsKey("insecure_ssl"));
Assert.Equal(request.Config["url"], "http://test.com/example");
Assert.Equal(request.Config["content_type"], OrgWebHookContentType.Form.ToParameter());
Assert.Equal(request.Config["secret"], "");
Assert.Equal(request.Config["insecure_ssl"], "False");
}
[Fact]
public void CombinesUserSpecifiedContentTypeWithConfig()
{
var config = new Dictionary<string, string>
{
{"hostname", "http://hostname.url"},
{"username", "username"},
{"password", "password"}
};
var create = new NewOrganizationWebHook("windowsazure", config, "http://test.com/example")
{
ContentType = OrgWebHookContentType.Json,
Secret = string.Empty,
InsecureSsl = true
};
Assert.Equal(create.Url, "http://test.com/example");
Assert.Equal(create.ContentType, OrgWebHookContentType.Json);
Assert.Empty(create.Secret);
Assert.True(create.InsecureSsl);
var request = create.ToRequest();
Assert.Equal(request.Config.Count, 7);
Assert.True(request.Config.ContainsKey("url"));
Assert.True(request.Config.ContainsKey("content_type"));
Assert.True(request.Config.ContainsKey("secret"));
Assert.True(request.Config.ContainsKey("insecure_ssl"));
Assert.Equal(request.Config["url"], "http://test.com/example");
Assert.Equal(request.Config["content_type"], OrgWebHookContentType.Json.ToParameter());
Assert.Equal(request.Config["secret"], "");
Assert.Equal(request.Config["insecure_ssl"], true.ToString());
Assert.True(request.Config.ContainsKey("hostname"));
Assert.Equal(request.Config["hostname"], config["hostname"]);
Assert.True(request.Config.ContainsKey("username"));
Assert.Equal(request.Config["username"], config["username"]);
Assert.True(request.Config.ContainsKey("password"));
Assert.Equal(request.Config["password"], config["password"]);
}
[Fact]
public void CanSetHookEvents()
{
var create = new NewOrganizationWebHook("web", new Dictionary<string, string>(), "http://test.com/example")
{
Events = new List<string> { "*" }
};
var request = create.ToRequest();
Assert.Contains("*", request.Events);
}
[Fact]
public void EnsureCanCallToRequestMultipleTimes()
{
var create = new NewOrganizationWebHook("web", new Dictionary<string, string>(), "http://test.com/example")
{
Events = new List<string> { "*" }
};
var request = create.ToRequest();
var requestRepeated = create.ToRequest();
Assert.Contains("*", request.Events);
Assert.Contains("*", requestRepeated.Events);
}
[Fact]
public void ShouldNotContainDuplicateConfigEntriesOnSubsequentRequests()
{
var create = new NewOrganizationWebHook("web", new Dictionary<string, string>(), "http://test.com/example");
var request = create.ToRequest();
var requestRepeated = create.ToRequest();
Assert.Equal(request.Config.Count, 4);
Assert.Equal(requestRepeated.Config.Count, 4);
}
[Fact]
public void ShouldNotContainDuplicateConfigEntriesOnSubsequentRequestsWithCustomisedConfig()
{
var config = new Dictionary<string, string>
{
{"url", "http://example.com/test"},
{"hostname", "http://hostname.url"},
{"username", "username"},
{"password", "password"}
};
var create = new NewOrganizationWebHook("web", config, "http://test.com/example");
var request = create.ToRequest();
var requestRepeated = create.ToRequest();
//This is not 8, because `url` used in config, is already part of the base config
Assert.Equal(request.Config.Count, 7);
Assert.Equal(requestRepeated.Config.Count, 7);
}
[Fact]
public void PropertiesShouldTakePrecedenceOverConfigPassedIn()
{
var config = new Dictionary<string, string>
{
{"url", "http://originalurl.com/test"},
};
var create = new NewOrganizationWebHook("web", config, "http://test.com/example");
var request = create.ToRequest();
Assert.Equal(request.Config["url"], "http://test.com/example");
var subsequentRequest = create.ToRequest();
Assert.Equal(subsequentRequest.Config["url"], "http://test.com/example");
}
}
}
}

View File

@@ -0,0 +1,187 @@
using System;
using System.Collections.Generic;
using NSubstitute;
using Octokit.Reactive;
using Xunit;
namespace Octokit.Tests.Reactive
{
public class ObservableOrganizationHooksClientTests
{
public class TheCtor
{
[Fact]
public void EnsuresNonNullArguments()
{
Assert.Throws<ArgumentNullException>(
() => new ObservableOrganizationHooksClient(null));
}
}
public class TheGetAllMethod
{
[Fact]
public void RequestsCorrectUrl()
{
var gitHubClient = Substitute.For<IGitHubClient>();
var client = new ObservableOrganizationHooksClient(gitHubClient);
client.GetAll("org");
gitHubClient.Received().Organization.Hook.GetAll("org");
}
[Fact]
public void RequestsCorrectUrlWithApiOptions()
{
var gitHubClient = Substitute.For<IGitHubClient>();
var client = new ObservableOrganizationHooksClient(gitHubClient);
var options = new ApiOptions
{
PageCount = 1,
PageSize = 1,
StartPage = 1
};
client.GetAll("org", options);
gitHubClient.Received(1).Organization.Hook.GetAll("org", options);
}
[Fact]
public void EnsuresNonNullArguments()
{
var client = new ObservableOrganizationHooksClient(Substitute.For<IGitHubClient>());
Assert.Throws<ArgumentNullException>(() => client.GetAll(null, ApiOptions.None));
Assert.Throws<ArgumentNullException>(() => client.GetAll("org", null));
Assert.Throws<ArgumentException>(() => client.GetAll(""));
Assert.Throws<ArgumentException>(() => client.GetAll("", null));
}
}
public class TheGetMethod
{
[Fact]
public void RequestsCorrectUrl()
{
var gitHubClient = Substitute.For<IGitHubClient>();
var client = new ObservableOrganizationHooksClient(gitHubClient);
client.Get("org", 12345678);
gitHubClient.Received().Organization.Hook.Get("org", 12345678);
}
[Fact]
public void EnsuresNonNullArguments()
{
var client = new ObservableOrganizationHooksClient(Substitute.For<IGitHubClient>());
Assert.Throws<ArgumentNullException>(() => client.Get(null, 123));
Assert.Throws<ArgumentException>(() => client.Get("", 123));
}
}
public class TheCreateMethod
{
[Fact]
public void RequestsCorrectUrl()
{
var gitHubClient = Substitute.For<IGitHubClient>();
var client = new ObservableOrganizationHooksClient(gitHubClient);
var hook = new NewOrganizationHook("name", new Dictionary<string, string> { { "config", "" } });
client.Create("org", hook);
gitHubClient.Received().Organization.Hook.Create("org", hook);
}
[Fact]
public void EnsuresNonNullArguments()
{
var client = new ObservableOrganizationHooksClient(Substitute.For<IGitHubClient>());
var config = new Dictionary<string, string> { { "config", "" } };
Assert.Throws<ArgumentNullException>(() => client.Create(null, new NewOrganizationHook("name", config)));
Assert.Throws<ArgumentNullException>(() => client.Create("org", null));
Assert.Throws<ArgumentException>(() => client.Create("", new NewOrganizationHook("name", config)));
}
}
public class TheEditMethod
{
[Fact]
public void RequestsCorrectUrl()
{
var gitHubClient = Substitute.For<IGitHubClient>();
var client = new ObservableOrganizationHooksClient(gitHubClient);
var hook = new EditOrganizationHook();
client.Edit("org", 12345678, hook);
gitHubClient.Received().Organization.Hook.Edit("org", 12345678, hook);
}
[Fact]
public void EnsuresNonNullArguments()
{
var client = new ObservableOrganizationHooksClient(Substitute.For<IGitHubClient>());
Assert.Throws<ArgumentNullException>(() => client.Edit(null, 12345678, new EditOrganizationHook()));
Assert.Throws<ArgumentNullException>(() => client.Edit("org", 12345678, null));
Assert.Throws<ArgumentException>(() => client.Edit("", 12345678, new EditOrganizationHook()));
}
}
public class ThePingMethod
{
[Fact]
public void RequestsCorrectUrl()
{
var gitHubClient = Substitute.For<IGitHubClient>();
var client = new ObservableOrganizationHooksClient(gitHubClient);
client.Ping("org", 12345678);
gitHubClient.Received().Organization.Hook.Ping("org", 12345678);
}
[Fact]
public void EnsuresNonNullArguments()
{
var client = new ObservableOrganizationHooksClient(Substitute.For<IGitHubClient>());
Assert.Throws<ArgumentNullException>(() => client.Ping(null, 12345678));
Assert.Throws<ArgumentException>(() => client.Ping("", 12345678));
}
}
public class TheDeleteMethod
{
[Fact]
public void RequestsCorrectUrl()
{
var gitHubClient = Substitute.For<IGitHubClient>();
var client = new ObservableOrganizationHooksClient(gitHubClient);
client.Delete("org", 12345678);
gitHubClient.Received().Organization.Hook.Delete("org", 12345678);
}
[Fact]
public void EnsuresNonNullArguments()
{
var client = new ObservableOrganizationHooksClient(Substitute.For<IGitHubClient>());
Assert.Throws<ArgumentNullException>(() => client.Delete(null, 12345678));
Assert.Throws<ArgumentException>(() => client.Delete("", 12345678));
}
}
}
}

View File

@@ -0,0 +1,72 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
namespace Octokit
{
/// <summary>
/// A client for GitHub's Organization Webhooks API.
/// </summary>
/// <remarks>
/// See the <a href="https://developer.github.com/v3/orgs/hooks/">Webhooks API documentation</a> for more information.
/// </remarks>
public interface IOrganizationHooksClient
{
/// <summary>
/// Gets the list of hooks defined for a organization
/// </summary>
/// <param name="org">The organizations name</param>
/// <remarks>See <a href="https://developer.github.com/v3/orgs/hooks/#list-hooks">API documentation</a> for more information.</remarks>
Task<IReadOnlyList<OrganizationHook>> GetAll(string org);
/// <summary>
/// Gets the list of hooks defined for a organization
/// </summary>
/// <param name="org">The organizations name</param>
/// <param name="options">Options for changing the API response</param>
/// <remarks>See <a href="https://developer.github.com/v3/orgs/hooks/#list-hooks">API documentation</a> for more information.</remarks>
Task<IReadOnlyList<OrganizationHook>> GetAll(string org, ApiOptions options);
/// <summary>
/// Gets a single hook by Id
/// </summary>
/// <param name="org">The organizations name</param>
/// <param name="hookId">The repository's hook id</param>
/// <remarks>See <a href="https://developer.github.com/v3/orgs/hooks/#get-single-hook">API documentation</a> for more information.</remarks>
[SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get", Justification = "This is ok; we're matching HTTP verbs not keywords")]
Task<OrganizationHook> Get(string org, int hookId);
/// <summary>
/// Creates a hook for a organization
/// </summary>
/// <param name="org">The organizations name</param>
/// <param name="hook">The hook's parameters</param>
/// <remarks>See <a href="https://developer.github.com/v3/orgs/hooks/#create-a-hook">API documentation</a> for more information.</remarks>
Task<OrganizationHook> Create(string org, NewOrganizationHook hook);
/// <summary>
/// Edits a hook for a organization
/// </summary>
/// <param name="org">The organizations name</param>
/// <param name="hookId">The organizations hook id</param>
/// <param name="hook">The hook's parameters</param>
/// <remarks>See <a href="https://developer.github.com/v3/orgs/hooks/#edit-a-hook">API documentation</a> for more information.</remarks>
Task<OrganizationHook> Edit(string org, int hookId, EditOrganizationHook hook);
/// <summary>
/// This will trigger a ping event to be sent to the hook.
/// </summary>
/// <param name="org">The organizations name</param>
/// <param name="hookId">The organizations hook id</param>
/// <remarks>See <a href="https://developer.github.com/v3/orgs/hooks/#ping-a-hook">API documentation</a> for more information.</remarks>
Task Ping(string org, int hookId);
/// <summary>
/// Deletes a hook for a organization
/// </summary>
/// <param name="org">The organizations name</param>
/// <param name="hookId">The organizations hook id</param>
/// <remarks>See <a href="https://developer.github.com/v3/orgs/hooks/#delete-a-hook">API documentation</a> for more information.</remarks>
Task Delete(string org, int hookId);
}
}

View File

@@ -24,6 +24,12 @@ namespace Octokit
/// </summary>
ITeamsClient Team { get; }
/// <summary>
/// A client for GitHub's Organization Hooks API.
/// </summary>
/// <remarks>See <a href="https://developer.github.com/v3/orgs/hooks/">Hooks API documentation</a> for more information.</remarks>
IOrganizationHooksClient Hook { get; }
/// <summary>
/// Returns a client to manage outside collaborators of an organization.
/// </summary>
@@ -94,10 +100,10 @@ namespace Octokit
/// <summary>
/// Update the specified organization with data from <see cref="OrganizationUpdate"/>.
/// </summary>
/// <param name="organizationName">The name of the organization to update.</param>
/// <param name="org">The name of the organization to update.</param>
/// <param name="updateRequest"></param>
/// <exception cref="AuthorizationException">Thrown if the client is not authenticated.</exception>
/// <returns>A <see cref="Organization"/></returns>
Task<Organization> Update(string organizationName, OrganizationUpdate updateRequest);
Task<Organization> Update(string org, OrganizationUpdate updateRequest);
}
}

View File

@@ -0,0 +1,118 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Octokit
{
public class OrganizationHooksClient : ApiClient, IOrganizationHooksClient
{
/// <summary>
/// Initializes a new GitHub Repos API client.
/// </summary>
/// <param name="apiConnection">An API connection.</param>
public OrganizationHooksClient(IApiConnection apiConnection)
: base(apiConnection)
{
}
/// <summary>
/// Gets the list of hooks defined for a organization
/// </summary>
/// <param name="org">The organization's name</param>
/// <remarks>See <a href="https://developer.github.com/v3/orgs/hooks/#list-hooks">API documentation</a> for more information.</remarks>
/// <returns></returns>
[ManualRoute("GET", "orgs/{org}/hooks")]
public Task<IReadOnlyList<OrganizationHook>> GetAll(string org)
{
Ensure.ArgumentNotNullOrEmptyString(org, nameof(org));
return ApiConnection.GetAll<OrganizationHook>(ApiUrls.OrganizationHooks(org));
}
/// <summary>
/// Gets the list of hooks defined for a organization
/// </summary>
/// <param name="org">The organization's name</param>
/// <param name="options">Options for changing the API response</param>
/// <remarks>See <a href="https://developer.github.com/v3/orgs/hooks/#list-hooks">API documentation</a> for more information.</remarks>
/// <returns></returns>
[ManualRoute("GET", "orgs/{org}/hooks")]
public Task<IReadOnlyList<OrganizationHook>> GetAll(string org, ApiOptions options)
{
Ensure.ArgumentNotNullOrEmptyString(org, nameof(org));
Ensure.ArgumentNotNull(options, nameof(options));
return ApiConnection.GetAll<OrganizationHook>(ApiUrls.OrganizationHooks(org), options);
}
/// <summary>
/// Gets a single hook by Id
/// </summary>
/// <param name="org"></param>
/// <param name="hookId"></param>
/// <returns></returns>
/// <remarks>See <a href="https://developer.github.com/v3/orgs/hooks/#get-single-hook">API documentation</a> for more information.</remarks>
[ManualRoute("GET", "orgs/{org}/hooks/{hook_id}")]
public Task<OrganizationHook> Get(string org, int hookId)
{
Ensure.ArgumentNotNullOrEmptyString(org, nameof(org));
Ensure.ArgumentNotNull(hookId, nameof(hookId));
return ApiConnection.Get<OrganizationHook>(ApiUrls.OrganizationHookById(org, hookId));
}
/// <summary>
/// Creates a hook for a organization
/// </summary>
/// <remarks>See <a href="https://developer.github.com/v3/orgs/hooks/#create-a-hook">API documentation</a> for more information.</remarks>
/// <returns></returns>
[ManualRoute("POST", "orgs/{org}/hooks")]
public Task<OrganizationHook> Create(string org, NewOrganizationHook hook)
{
Ensure.ArgumentNotNullOrEmptyString(org, nameof(org));
Ensure.ArgumentNotNull(hook, nameof(hook));
return ApiConnection.Post<OrganizationHook>(ApiUrls.OrganizationHooks(org), hook.ToRequest());
}
/// <summary>
/// Edits a hook for a organization
/// </summary>
/// <remarks>See <a href="https://developer.github.com/v3/orgs/hooks/#edit-a-hook">API documentation</a> for more information.</remarks>
/// <returns></returns>
[ManualRoute("PATCH", "orgs/{org}/hooks/{hook_id}")]
public Task<OrganizationHook> Edit(string org, int hookId, EditOrganizationHook hook)
{
Ensure.ArgumentNotNullOrEmptyString(org, nameof(org));
Ensure.ArgumentNotNull(hook, nameof(hook));
return ApiConnection.Patch<OrganizationHook>(ApiUrls.OrganizationHookById(org, hookId), hook);
}
/// <summary>
/// This will trigger a ping event to be sent to the hook.
/// </summary>
/// <remarks>See <a href="https://developer.github.com/v3/orgs/hooks/#ping-a-hook">API documentation</a> for more information.</remarks>
/// <returns></returns>
[ManualRoute("POST", "orgs/{org}/hooks/{hook_id}/pings")]
public Task Ping(string org, int hookId)
{
Ensure.ArgumentNotNullOrEmptyString(org, nameof(org));
Ensure.ArgumentNotNull(hookId, nameof(hookId));
return ApiConnection.Post(ApiUrls.OrganizationHookPing(org, hookId));
}
/// <summary>
/// Deletes a hook for a organization
/// </summary>
/// <remarks>See <a href="https://developer.github.com/v3/orgs/hooks/#delete-a-hook">API documentation</a> for more information.</remarks>
/// <returns></returns>
[ManualRoute("DELETE", "orgs/{org}/hooks/{hook_id}")]
public Task Delete(string org, int hookId)
{
Ensure.ArgumentNotNullOrEmptyString(org, nameof(org));
Ensure.ArgumentNotNull(hookId, nameof(hookId));
return ApiConnection.Delete(ApiUrls.OrganizationHookById(org, hookId));
}
}
}

View File

@@ -20,6 +20,7 @@ namespace Octokit
{
Member = new OrganizationMembersClient(apiConnection);
Team = new TeamsClient(apiConnection);
Hook = new OrganizationHooksClient(apiConnection);
OutsideCollaborator = new OrganizationOutsideCollaboratorsClient(apiConnection);
}
@@ -52,6 +53,12 @@ namespace Octokit
return ApiConnection.Get<Organization>(ApiUrls.Organization(org));
}
/// <summary>
/// A client for GitHub's Organization Hooks API.
/// </summary>
/// <remarks>See <a href="https://developer.github.com/v3/orgs/hooks/">Hooks API documentation</a> for more information.</remarks>
public IOrganizationHooksClient Hook { get; private set; }
/// <summary>
/// Returns all <see cref="Organization" />s for the current user.
/// </summary>
@@ -138,17 +145,17 @@ namespace Octokit
/// <summary>
/// Update the specified organization with data from <see cref="OrganizationUpdate"/>.
/// </summary>
/// <param name="organizationName">The name of the organization to update.</param>
/// <param name="org">The name of the organization to update.</param>
/// <param name="updateRequest"></param>
/// <exception cref="AuthorizationException">Thrown if the client is not authenticated.</exception>
/// <returns>A <see cref="Organization"/></returns>
[ManualRoute("PATCH", "/orgs/{org}")]
public Task<Organization> Update(string organizationName, OrganizationUpdate updateRequest)
public Task<Organization> Update(string org, OrganizationUpdate updateRequest)
{
Ensure.ArgumentNotNullOrEmptyString(organizationName, nameof(organizationName));
Ensure.ArgumentNotNullOrEmptyString(org, nameof(org));
Ensure.ArgumentNotNull(updateRequest, nameof(updateRequest));
var updateUri = new Uri("orgs/" + organizationName, UriKind.Relative);
var updateUri = new Uri("orgs/" + org, UriKind.Relative);
return ApiConnection.Patch<Organization>(updateUri, updateRequest);
}

View File

@@ -985,6 +985,38 @@ namespace Octokit
return "repos/{0}/{1}/hooks/{2}/pings".FormatUri(owner, name, hookId);
}
/// <summary>
/// Returns the <see cref="Uri"/> that lists the organization hooks for the specified reference.
/// </summary>
/// <param name="org">The name of the organization</param>
/// <returns></returns>
public static Uri OrganizationHooks(string org)
{
return "orgs/{0}/hooks".FormatUri(org);
}
/// <summary>
/// Returns the <see cref="Uri"/> that gets the organization hook for the specified reference.
/// </summary>
/// <param name="org">The name of the organization</param>
/// <param name="hookId">The identifier of the organization hook</param>
/// <returns></returns>
public static Uri OrganizationHookById(string org, int hookId)
{
return "orgs/{0}/hooks/{1}".FormatUri(org, hookId);
}
/// <summary>
/// Returns the <see cref="Uri"/> that can ping a specified organization hook
/// </summary>
/// <param name="org">The name of the organization</param>
/// <param name="hookId">The identifier of the organization hook</param>
/// <returns></returns>
public static Uri OrganizationHookPing(string org, int hookId)
{
return "orgs/{0}/hooks/{1}/pings".FormatUri(org, hookId);
}
/// <summary>
/// Returns the <see cref="Uri"/> that lists the commit statuses for the specified reference.
/// </summary>

View File

@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Octokit.Internal;
namespace Octokit
{
/// <summary>
/// Represents the requested changes to an edit repository hook.
/// </summary>
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class EditOrganizationHook
{
/// <summary>
/// Initializes a new instance of the <see cref="EditOrganizationHook"/> class.
/// </summary>
public EditOrganizationHook() : this(null)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="EditOrganizationHook"/> class.
/// </summary>
/// <param name="config">The configuration.</param>
public EditOrganizationHook(IDictionary<string, string> config)
{
Config = config;
}
public IDictionary<string, string> Config { get; private set; }
/// <summary>
/// Gets or sets the events.
/// </summary>
/// <value>
/// The events.
/// </value>
public IEnumerable<string> Events { get; set; }
/// <summary>
/// Gets or sets the active.
/// </summary>
/// <value>
/// The active.
/// </value>
public bool? Active { get; set; }
internal string DebuggerDisplay
{
get
{
return string.Format(CultureInfo.InvariantCulture,
"Organizaton Hook: Events: {0}", Events == null ? "no" : string.Join(", ", Events));
}
}
}
}

View File

@@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
namespace Octokit
{
/// <summary>
/// Creates a Webhook for the organization.
/// </summary>
/// <remarks>
/// To create a webhook, the following fields are required by the config:
/// <list type="bullet">
/// <item>
/// <term>url</term>
/// <description>A required string defining the URL to which the payloads will be delivered.</description>
/// </item>
/// <item>
/// <term>content_type</term>
/// <description>
/// An optional string defining the media type used to serialize the payloads. Supported values include json and
/// form. The default is form.
/// </description>
/// </item>
/// <item>
/// <term>secret</term>
/// <description>
/// An optional string thats passed with the HTTP requests as an X-Hub-Signature header. The value of this
/// header is computed as the HMAC hex digest of the body, using the secret as the key.
/// </description>
/// </item>
/// <item>
/// <term>insecure_ssl:</term>
/// <description>
/// An optional string that determines whether the SSL certificate of the host for url will be verified when
/// delivering payloads. Supported values include "0" (verification is performed) and "1" (verification is not
/// performed). The default is "0".
/// </description>
/// </item>
/// </list>
/// <para>
/// API: https://developer.github.com/v3/repos/hooks/#create-a-hook
/// </para>
/// </remarks>
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class NewOrganizationHook
{
/// <summary>
/// Initializes a new instance of the <see cref="NewOrganizationHook"/> class.
/// </summary>
/// <param name="name">
/// Use "web" for a webhook or use the name of a valid service. (See
/// <see href="https://api.github.com/hooks">https://api.github.com/hooks</see> for the list of valid service
/// names.)
/// </param>
/// <param name="config">
/// Key/value pairs to provide settings for this hook. These settings vary between the services and are
/// defined in the github-services repository. Booleans are stored internally as “1” for true, and “0” for
/// false. Any JSON true/false values will be converted automatically.
/// </param>
public NewOrganizationHook(string name, IReadOnlyDictionary<string, string> config)
{
Name = name;
Config = config;
}
/// <summary>
/// Gets the name of the hook to create. Use "web" for a webhook or use the name of a valid service. (See
/// <see href="https://api.github.com/hooks">https://api.github.com/hooks</see> for the list of valid service
/// names.)
/// </summary>
/// <value>
/// The name.
/// </value>
public string Name { get; private set; }
/// <summary>
/// Key/value pairs to provide settings for this hook. These settings vary between the services and are
/// defined in the github-services repository. Booleans are stored internally as “1” for true, and “0” for
/// false. Any JSON true/false values will be converted automatically.
/// </summary>
/// <value>
/// The configuration.
/// </value>
public IReadOnlyDictionary<string, string> Config { get; protected set; }
/// <summary>
/// Determines what events the hook is triggered for. Default: ["push"]
/// </summary>
/// <value>
/// The events.
/// </value>
public IEnumerable<string> Events { get; set; }
/// <summary>
/// Determines whether the hook is actually triggered on pushes.
/// </summary>
/// <value>
/// <c>true</c> if active; otherwise, <c>false</c>.
/// </value>
public bool Active { get; set; }
public virtual NewOrganizationHook ToRequest()
{
return this;
}
internal string DebuggerDisplay
{
get
{
return string.Format(CultureInfo.InvariantCulture,
"Organization Hook: Name: {0}, Events: {1}", Name, string.Join(", ", Events));
}
}
}
}

View File

@@ -0,0 +1,139 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace Octokit
{
/// <summary>
/// Creates a Webhook for the repository.
/// </summary>
/// <remarks>
/// To create a webhook, the following fields are required by the config:
/// <list type="bullet">
/// <item>
/// <term>url</term>
/// <description>A required string defining the URL to which the payloads will be delivered.</description>
/// </item>
/// <item>
/// <term>content_type</term>
/// <description>
/// An optional string defining the media type used to serialize the payloads. Supported values include json and
/// form. The default is form.
/// </description>
/// </item>
/// <item>
/// <term>secret</term>
/// <description>
/// An optional string thats passed with the HTTP requests as an X-Hub-Signature header. The value of this
/// header is computed as the HMAC hex digest of the body, using the secret as the key.
/// </description>
/// </item>
/// <item>
/// <term>insecure_ssl:</term>
/// <description>
/// An optional string that determines whether the SSL certificate of the host for url will be verified when
/// delivering payloads. Supported values include "0" (verification is performed) and "1" (verification is not
/// performed). The default is "0".
/// </description>
/// </item>
/// </list>
/// <para>
/// API: https://developer.github.com/v3/repos/hooks/#create-a-hook
/// </para>
/// </remarks>
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class NewOrganizationWebHook : NewOrganizationHook
{
/// <summary>
/// Initializes a new instance of the <see cref="NewOrganizationWebHook"/> class.
/// Using default values for ContentType, Secret and InsecureSsl.
/// </summary>
/// <param name="name">
/// Use "web" for a webhook or use the name of a valid service. (See
/// <see href="https://api.github.com/hooks">https://api.github.com/hooks</see> for the list of valid service
/// names.)
/// </param>
/// <param name="config">
/// Key/value pairs to provide settings for this hook. These settings vary between the services and are
/// defined in the github-services repository. Booleans are stored internally as “1” for true, and “0” for
/// false. Any true/false values will be converted automatically.
/// </param>
/// <param name="url">
/// A required string defining the URL to which the payloads will be delivered.
/// </param>
public NewOrganizationWebHook(string name, IReadOnlyDictionary<string, string> config, string url)
: base(name, config)
{
Ensure.ArgumentNotNullOrEmptyString(url, "url");
Url = url;
ContentType = OrgWebHookContentType.Form;
Secret = "";
InsecureSsl = false;
}
/// <summary>
/// Gets the URL of the hook to create.
/// </summary>
/// <value>
/// The URL.
/// </value>
public string Url { get; protected set; }
/// <summary>
/// Gets the content type used to serialize the payload. The default is `form`.
/// </summary>
/// <value>
/// The content type.
/// </value>
public OrgWebHookContentType ContentType { get; set; }
/// <summary>
/// Gets the secret used as the key for the HMAC hex digest
/// of the body passed with the HTTP requests as an X-Hub-Signature header.
/// </summary>
/// <value>
/// The secret.
/// </value>
public string Secret { get; set; }
/// <summary>
/// Gets whether the SSL certificate of the host will be verified when
/// delivering payloads. The default is `false`.
/// </summary>
/// <value>
/// <c>true</c> if SSL certificate verification is not performed;
/// otherwise, <c>false</c>.
/// </value>
public bool InsecureSsl { get; set; }
public override NewOrganizationHook ToRequest()
{
Config = GetWebHookConfig()
.Union(Config, new WebHookConfigComparer())
.ToDictionary(k => k.Key, v => v.Value);
return this;
}
Dictionary<string, string> GetWebHookConfig()
{
return new Dictionary<string, string>
{
{ "url", Url },
{ "content_type", ContentType.ToParameter() },
{ "secret", Secret },
{ "insecure_ssl", InsecureSsl.ToString() }
};
}
}
/// <summary>
/// The supported content types for payload serialization.
/// </summary>
public enum OrgWebHookContentType
{
Form,
Json
}
}

View File

@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Octokit.Internal;
namespace Octokit
{
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class OrganizationHook
{
public OrganizationHook() { }
public OrganizationHook(int id, string url, string testUrl, string pingUrl, DateTimeOffset createdAt, DateTimeOffset updatedAt, string name, IReadOnlyList<string> events, bool active, IReadOnlyDictionary<string, string> config)
{
Url = url;
TestUrl = testUrl;
PingUrl = pingUrl;
CreatedAt = createdAt;
UpdatedAt = updatedAt;
Name = name;
Events = events;
Active = active;
Config = config;
Id = id;
}
public int Id { get; private set; }
public string Url { get; private set; }
[Parameter(Key = "test_url")]
public string TestUrl { get; private set; }
[Parameter(Key = "ping_url")]
public string PingUrl { get; private set; }
public DateTimeOffset CreatedAt { get; private set; }
public DateTimeOffset UpdatedAt { get; private set; }
public string Name { get; private set; }
public IReadOnlyList<string> Events { get; private set; }
public bool Active { get; private set; }
public IReadOnlyDictionary<string, string> Config { get; private set; }
internal string DebuggerDisplay
{
get
{
return string.Format(CultureInfo.InvariantCulture,
"Organization Hook: Name: {0} Url: {1}, Events: {2}", Name, Url, string.Join(", ", Events));
}
}
}
}