Skip to content

Commit

Permalink
Merge pull request octokit#917 from alfhenrik/feature-webhookhelper
Browse files Browse the repository at this point in the history
Add helper class for creating web hooks
  • Loading branch information
shiftkey committed Nov 11, 2015
2 parents f0111aa + e643534 commit ddcfc0a
Show file tree
Hide file tree
Showing 15 changed files with 395 additions and 12 deletions.
30 changes: 23 additions & 7 deletions Octokit.Tests.Integration/Clients/RepositoryHooksClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,35 +63,51 @@ public async Task CreateAWebHookForTestRepository()
var repoName = Helper.MakeNameWithTimestamp("create-hooks-test");
var repository = await github.Repository.Create(new NewRepository(repoName) { AutoInit = true });

var url = "http://test.com/example";
var contentType = WebHookContentType.Json;
var secret = "53cr37";
var config = new Dictionary<string, string>
{
{ "content_type", "json" },
{ "url", "http://test.com/example" },
{ "hostname", "http://hostname.url" },
{ "username", "username" },
{ "password", "password" }
};
var parameters = new NewRepositoryHook("windowsazure", config)
var parameters = new NewRepositoryWebHook("windowsazure", config, url)
{
Events = new[] { "push" },
Active = false
Active = false,
ContentType = contentType,
Secret = secret
};

var hook = await github.Repository.Hooks.Create(Helper.Credentials.Login, repository.Name, parameters);
var hook = await github.Repository.Hooks.Create(Helper.Credentials.Login, repository.Name, parameters.ToRequest());

var baseHookUrl = CreateExpectedBaseHookUrl(repository.Url, hook.Id);
var webHookConfig = CreateExpectedConfigDictionary(config, url, contentType, secret);

Assert.Equal("windowsazure", hook.Name);
Assert.Equal(new[] { "push" }.ToList(), hook.Events.ToList());
Assert.Equal(baseHookUrl, hook.Url);
Assert.Equal(baseHookUrl + "/test", hook.TestUrl);
Assert.Equal(baseHookUrl + "/pings", hook.PingUrl);
Assert.NotNull(hook.CreatedAt);
Assert.NotNull(hook.UpdatedAt);
Assert.Equal(config.Keys, hook.Config.Keys);
Assert.Equal(config.Values, hook.Config.Values);
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, WebHookContentType contentType, string secret)
{
return config.Union(new Dictionary<string, string>
{
{ "url", url },
{ "content_type", contentType.ToString().ToLowerInvariant() },
{ "secret", secret },
{ "insecure_ssl", "False" }
}).ToDictionary(k => k.Key, v => v.Value);
}

string CreateExpectedBaseHookUrl(string url, int id)
{
return url + "/hooks/" + id;
Expand Down
108 changes: 108 additions & 0 deletions Octokit.Tests/Models/NewRepositoryWebHookTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using System.Collections.Generic;
using Xunit;

namespace Octokit.Tests.Models
{
public class NewRepositoryWebHookTests
{
public class TheCtor
{
string ExpectedRepositoryWebHookConfigExceptionMessage =
"Duplicate webhook config values found - these values: Url should not be passed in as part of the config values. Use the properties on the NewRepositoryWebHook class instead.";

[Fact]
public void UsesDefaultValuesForDefaultConfig()
{
var create = new NewRepositoryWebHook("windowsazure", new Dictionary<string, string>(), "http://test.com/example");
Assert.Equal(create.Url, "http://test.com/example");
Assert.Equal(create.ContentType, WebHookContentType.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"], WebHookContentType.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 NewRepositoryWebHook("windowsazure", config, "http://test.com/example")
{
ContentType = WebHookContentType.Json,
Secret = string.Empty,
InsecureSsl = true
};

Assert.Equal(create.Url, "http://test.com/example");
Assert.Equal(create.ContentType, WebHookContentType.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"], WebHookContentType.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 ShouldThrowRepositoryWebHookConfigExceptionWhenDuplicateKeysExists()
{
var config = new Dictionary<string, string>
{
{"url", "http://example.com/test"},
{"hostname", "http://hostname.url"},
{"username", "username"},
{"password", "password"}
};

var create = new NewRepositoryWebHook("windowsazure", config, "http://test.com/example")
{
ContentType = WebHookContentType.Json,
Secret = string.Empty,
InsecureSsl = true
};

Assert.Equal(create.Url, "http://test.com/example");
Assert.Equal(create.ContentType, WebHookContentType.Json);
Assert.Empty(create.Secret);
Assert.True(create.InsecureSsl);

var ex = Assert.Throws<RepositoryWebHookConfigException>(() => create.ToRequest());
Assert.Equal(ExpectedRepositoryWebHookConfigExceptionMessage, ex.Message);
}
}
}
}
1 change: 1 addition & 0 deletions Octokit.Tests/Octokit.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@
<Compile Include="Models\MilestoneRequestTests.cs" />
<Compile Include="Models\IssueRequestTests.cs" />
<Compile Include="Models\ModelExtensionsTests.cs" />
<Compile Include="Models\NewRepositoryWebHookTests.cs" />
<Compile Include="Models\PullRequestRequestTests.cs" />
<Compile Include="Models\PunchCardTests.cs" />
<Compile Include="Models\ReadOnlyPagedCollectionTests.cs" />
Expand Down
2 changes: 1 addition & 1 deletion Octokit/Clients/RepositoryHooksClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public Task<RepositoryHook> Create(string owner, string repositoryName, NewRepos
Ensure.ArgumentNotNullOrEmptyString(repositoryName, "repositoryName");
Ensure.ArgumentNotNull(hook, "hook");

return ApiConnection.Post<RepositoryHook>(ApiUrls.RepositoryHooks(owner, repositoryName), hook);
return ApiConnection.Post<RepositoryHook>(ApiUrls.RepositoryHooks(owner, repositoryName), hook.ToRequest());
}

/// <summary>
Expand Down
60 changes: 60 additions & 0 deletions Octokit/Exceptions/RepositoryWebHookConfigException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace Octokit
{
#if !NETFX_CORE
[Serializable]
#endif
[SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors",
Justification = "These exceptions are specific to the GitHub API and not general purpose exceptions")]
public class RepositoryWebHookConfigException : Exception
{
readonly string message;

public RepositoryWebHookConfigException(IEnumerable<string> invalidConfig)
{
var parameterList = string.Join(", ", invalidConfig.Select(ic => ic.FromRubyCase()));
message = string.Format(CultureInfo.InvariantCulture,
"Duplicate webhook config values found - these values: {0} should not be passed in as part of the config values. Use the properties on the NewRepositoryWebHook class instead.",
parameterList);
}

public override string Message
{
get { return message; }
}

#if !NETFX_CORE
/// <summary>
/// Constructs an instance of RepositoryWebHookConfigException
/// </summary>
/// <param name="info">
/// The <see cref="SerializationInfo"/> that holds the
/// serialized object data about the exception being thrown.
/// </param>
/// <param name="context">
/// The <see cref="StreamingContext"/> that contains
/// contextual information about the source or destination.
/// </param>
protected RepositoryWebHookConfigException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
if (info == null) return;
message = info.GetString("Message");
}

public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue("Message", Message);
}
#endif
}
}
11 changes: 11 additions & 0 deletions Octokit/Helpers/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,17 @@ public static string ToRubyCase(this string propertyName)
return string.Join("_", propertyName.SplitUpperCase()).ToLowerInvariant();
}

public static string FromRubyCase(this string propertyName)
{
Ensure.ArgumentNotNullOrEmptyString(propertyName, "s");
return string.Join("", propertyName.Split('_')).ToCapitalizedInvariant();
}

public static string ToCapitalizedInvariant(this string value)
{
Ensure.ArgumentNotNullOrEmptyString(value, "s");
return string.Concat(value[0].ToString().ToUpperInvariant(), value.Substring(1));
}
static IEnumerable<string> SplitUpperCase(this string source)
{
Ensure.ArgumentNotNullOrEmptyString(source, "source");
Expand Down
17 changes: 17 additions & 0 deletions Octokit/Helpers/WebHookConfigComparer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Collections.Generic;

namespace Octokit
{
public class WebHookConfigComparer : IEqualityComparer<KeyValuePair<string, string>>
{
public bool Equals(KeyValuePair<string, string> x, KeyValuePair<string, string> y)
{
return x.Key == y.Key;
}

public int GetHashCode(KeyValuePair<string, string> obj)
{
return obj.Key.GetHashCode();
}
}
}
11 changes: 8 additions & 3 deletions Octokit/Models/Request/NewRepositoryHook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ namespace Octokit
/// <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.
/// 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>
Expand All @@ -33,7 +33,7 @@ namespace Octokit
/// <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
/// delivering payloads. Supported values include "0" (verification is performed) and "1" (verification is not
/// performed). The default is "0".
/// </description>
/// </item>
Expand Down Expand Up @@ -100,6 +100,11 @@ public NewRepositoryHook(string name, IReadOnlyDictionary<string, string> config
/// </value>
public bool Active { get; set; }

public virtual NewRepositoryHook ToRequest()
{
return this;
}

internal string DebuggerDisplay
{
get
Expand Down
Loading

0 comments on commit ddcfc0a

Please sign in to comment.