diff --git a/Octokit/Exceptions/RepositoryWebHookConfigException.cs b/Octokit/Exceptions/RepositoryWebHookConfigException.cs new file mode 100644 index 00000000..2368e6c7 --- /dev/null +++ b/Octokit/Exceptions/RepositoryWebHookConfigException.cs @@ -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 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 + /// + /// Constructs an instance of RepositoryWebHookConfigException + /// + /// + /// The that holds the + /// serialized object data about the exception being thrown. + /// + /// + /// The that contains + /// contextual information about the source or destination. + /// + 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 + } +} diff --git a/Octokit/Helpers/StringExtensions.cs b/Octokit/Helpers/StringExtensions.cs index 71df4326..f195d405 100644 --- a/Octokit/Helpers/StringExtensions.cs +++ b/Octokit/Helpers/StringExtensions.cs @@ -84,6 +84,17 @@ namespace Octokit 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 SplitUpperCase(this string source) { Ensure.ArgumentNotNullOrEmptyString(source, "source"); diff --git a/Octokit/Models/Request/NewRepositoryWebHook.cs b/Octokit/Models/Request/NewRepositoryWebHook.cs index 2c73d06c..3be98b99 100644 --- a/Octokit/Models/Request/NewRepositoryWebHook.cs +++ b/Octokit/Models/Request/NewRepositoryWebHook.cs @@ -62,7 +62,7 @@ namespace Octokit /// /// A required string defining the URL to which the payloads will be delivered. /// - public NewRepositoryWebHook(string name, IReadOnlyDictionary config, string url) + public NewRepositoryWebHook(string name, IReadOnlyDictionary config, string url) : base(name, config) { Ensure.ArgumentNotNullOrEmptyString(url, "url"); @@ -88,7 +88,7 @@ namespace Octokit /// The content type. /// public WebHookContentType ContentType { get; set; } - + /// /// 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. @@ -97,7 +97,7 @@ namespace Octokit /// The secret. /// public string Secret { get; set; } - + /// /// Gets whether the SSL certificate of the host will be verified when /// delivering payloads. The default is `false`. @@ -110,15 +110,30 @@ namespace Octokit public override NewRepositoryHook ToRequest() { - var config = Config.Union(new Dictionary + var webHookConfig = GetWebHookConfig(); + if (Config.Any(c => webHookConfig.ContainsKey(c.Key))) { - { "url", Url }, - { "content_type", ContentType.ToParameter() }, - { "secret", Secret }, - { "insecure_ssl", InsecureSsl.ToString() } - }).ToDictionary(k => k.Key, v => v.Value); + var invalidConfigs = Config.Where(c => webHookConfig.ContainsKey(c.Key)).Select(c => c.Key); + throw new RepositoryWebHookConfigException(invalidConfigs); + } + + var config = webHookConfig + .Union(Config, new WebHookConfigComparer()) + .ToDictionary(k => k.Key, v => v.Value); + return new NewRepositoryHook(Name, config); } + + Dictionary GetWebHookConfig() + { + return new Dictionary + { + { "url", Url }, + { "content_type", ContentType.ToParameter() }, + { "secret", Secret }, + { "insecure_ssl", InsecureSsl.ToString() } + }; + } } /// @@ -129,4 +144,17 @@ namespace Octokit Form, Json } + + public class WebHookConfigComparer : IEqualityComparer> + { + public bool Equals(KeyValuePair x, KeyValuePair y) + { + return x.Key == y.Key; + } + + public int GetHashCode(KeyValuePair obj) + { + return obj.Key.GetHashCode(); + } + } } diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index ec28e293..4134099d 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -407,6 +407,7 @@ + \ No newline at end of file diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index 555f68cd..65b473d7 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -414,6 +414,7 @@ + diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index 9ffaf4ba..9f737839 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -410,7 +410,8 @@ + - + \ No newline at end of file diff --git a/Octokit/Octokit-Portable.csproj b/Octokit/Octokit-Portable.csproj index 5d4fb932..61de2d7b 100644 --- a/Octokit/Octokit-Portable.csproj +++ b/Octokit/Octokit-Portable.csproj @@ -404,6 +404,7 @@ + diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index c5f72441..cc584a7d 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -411,6 +411,7 @@ + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index e559317d..c0edf9d4 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -76,6 +76,7 @@ +