diff --git a/src/Umbraco.Core/Configuration/HealthChecks/BaseNotificationMethodElement.cs b/src/Umbraco.Core/Configuration/HealthChecks/BaseNotificationMethodElement.cs deleted file mode 100644 index b4c27e9d38..0000000000 --- a/src/Umbraco.Core/Configuration/HealthChecks/BaseNotificationMethodElement.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Configuration; - -namespace Umbraco.Core.Configuration.HealthChecks -{ - public abstract class BaseNotificationMethodElement : ConfigurationElement - { - private const string VERBOSITY_KEY = "verbosity"; - private const string FAILUREONLY_KEY = "failureOnly"; - - [ConfigurationProperty(VERBOSITY_KEY, IsRequired = true)] - public HealthCheckNotificationVerbosity Verbosity - { - get - { - return ((HealthCheckNotificationVerbosity)(base[VERBOSITY_KEY])); - } - } - - [ConfigurationProperty(FAILUREONLY_KEY, IsRequired = true)] - public bool FailureOnly - { - get - { - return ((bool)(base[FAILUREONLY_KEY])); - } - } - } -} diff --git a/src/Umbraco.Core/Configuration/HealthChecks/EmailSettingsElement.cs b/src/Umbraco.Core/Configuration/HealthChecks/EmailSettingsElement.cs deleted file mode 100644 index d9f8b22738..0000000000 --- a/src/Umbraco.Core/Configuration/HealthChecks/EmailSettingsElement.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Configuration; - -namespace Umbraco.Core.Configuration.HealthChecks -{ - public class EmailSettingsElement : BaseNotificationMethodElement - { - private const string RECIPIENT_EMAIL_KEY = "recipientEmail"; - private const string SUBJECT_KEY = "subject"; - - [ConfigurationProperty(RECIPIENT_EMAIL_KEY, IsRequired = true)] - public string RecipientEmail - { - get - { - return ((string)(base[RECIPIENT_EMAIL_KEY])); - } - } - - [ConfigurationProperty(SUBJECT_KEY, IsRequired = true)] - public string Subject - { - get - { - return ((string)(base[SUBJECT_KEY])); - } - } - } -} diff --git a/src/Umbraco.Core/Configuration/HealthChecks/HealthCheckNotificationSettingsElement.cs b/src/Umbraco.Core/Configuration/HealthChecks/HealthCheckNotificationSettingsElement.cs index 0022d5d565..6628d260d1 100644 --- a/src/Umbraco.Core/Configuration/HealthChecks/HealthCheckNotificationSettingsElement.cs +++ b/src/Umbraco.Core/Configuration/HealthChecks/HealthCheckNotificationSettingsElement.cs @@ -11,16 +11,15 @@ namespace Umbraco.Core.Configuration.HealthChecks private const string ENABLED_KEY = "enabled"; private const string FIRST_RUN_TIME_KEY = "firstRunTime"; private const string PERIOD_KEY = "periodInHours"; + private const string NOTIFICATION_METHODS_KEY = "notificationMethods"; private const string DISABLED_CHECKS_KEY = "disabledChecks"; - private const string EMAIL_SETTINGS_KEY = "emailSettings"; - private const string SLACK_SETTINGS_KEY = "slackSettings"; [ConfigurationProperty(ENABLED_KEY, IsRequired = true)] public bool Enabled { get { - return ((bool)(base[ENABLED_KEY])); + return (bool)base[ENABLED_KEY]; } } @@ -29,7 +28,7 @@ namespace Umbraco.Core.Configuration.HealthChecks { get { - return ((string)(base[FIRST_RUN_TIME_KEY])); + return (string)base[FIRST_RUN_TIME_KEY]; } } @@ -38,34 +37,25 @@ namespace Umbraco.Core.Configuration.HealthChecks { get { - return ((int)(base[PERIOD_KEY])); + return (int)base[PERIOD_KEY]; } } - [ConfigurationProperty(DISABLED_CHECKS_KEY, IsDefaultCollection = true, IsRequired = false)] + [ConfigurationProperty(NOTIFICATION_METHODS_KEY, IsDefaultCollection = true, IsRequired = false)] + public NotificationMethodsElementCollection NotificationMethods + { + get + { + return (NotificationMethodsElementCollection)base[NOTIFICATION_METHODS_KEY]; + } + } + + [ConfigurationProperty(DISABLED_CHECKS_KEY, IsDefaultCollection = false, IsRequired = false)] public DisabledHealthChecksElementCollection DisabledChecks { get { - return ((DisabledHealthChecksElementCollection)(base[DISABLED_CHECKS_KEY])); - } - } - - [ConfigurationProperty(EMAIL_SETTINGS_KEY, IsDefaultCollection = true, IsRequired = false)] - public EmailSettingsElement EmailSettings - { - get - { - return ((EmailSettingsElement)(base[EMAIL_SETTINGS_KEY])); - } - } - - [ConfigurationProperty(SLACK_SETTINGS_KEY, IsDefaultCollection = true, IsRequired = false)] - public SlackSettingsElement SlackSettings - { - get - { - return ((SlackSettingsElement)(base[SLACK_SETTINGS_KEY])); + return (DisabledHealthChecksElementCollection)base[DISABLED_CHECKS_KEY]; } } } diff --git a/src/Umbraco.Core/Configuration/HealthChecks/NotificationMethodElement.cs b/src/Umbraco.Core/Configuration/HealthChecks/NotificationMethodElement.cs new file mode 100644 index 0000000000..4276efc9af --- /dev/null +++ b/src/Umbraco.Core/Configuration/HealthChecks/NotificationMethodElement.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Linq; +using System.Text; + +namespace Umbraco.Core.Configuration.HealthChecks +{ + public class NotificationMethodElement : ConfigurationElement + { + private const string ALIAS_KEY = "alias"; + private const string ENABLED_KEY = "enabled"; + private const string VERBOSITY_KEY = "verbosity"; + private const string FAILUREONLY_KEY = "failureOnly"; + private const string SETTINGS_KEY = "settings"; + + [ConfigurationProperty(ALIAS_KEY, IsKey = true, IsRequired = true)] + public string Alias + { + get + { + return (string)base[ALIAS_KEY]; + } + } + + [ConfigurationProperty(ENABLED_KEY, IsKey = true, IsRequired = true)] + public bool Enabled + { + get + { + return (bool)base[ENABLED_KEY]; + } + } + + [ConfigurationProperty(VERBOSITY_KEY, IsRequired = true)] + public HealthCheckNotificationVerbosity Verbosity + { + get + { + return (HealthCheckNotificationVerbosity)base[VERBOSITY_KEY]; + } + } + + [ConfigurationProperty(FAILUREONLY_KEY, IsRequired = false)] + public bool FailureOnly + { + get + { + return (bool)base[FAILUREONLY_KEY]; + } + } + + [ConfigurationProperty(SETTINGS_KEY, IsDefaultCollection = true, IsRequired = false)] + public NotificationMethodSettingsElementCollection Settings + { + get + { + return (NotificationMethodSettingsElementCollection)base[SETTINGS_KEY]; + } + } + } +} diff --git a/src/Umbraco.Core/Configuration/HealthChecks/NotificationMethodSettingsElement.cs b/src/Umbraco.Core/Configuration/HealthChecks/NotificationMethodSettingsElement.cs new file mode 100644 index 0000000000..6431d14062 --- /dev/null +++ b/src/Umbraco.Core/Configuration/HealthChecks/NotificationMethodSettingsElement.cs @@ -0,0 +1,28 @@ +using System.Configuration; + +namespace Umbraco.Core.Configuration.HealthChecks +{ + public class NotificationMethodSettingsElement : ConfigurationElement + { + private const string KEY_KEY = "key"; + private const string VALUE_KEY = "value"; + + [ConfigurationProperty(KEY_KEY, IsKey = true, IsRequired = true)] + public string Key + { + get + { + return (string)base[KEY_KEY]; + } + } + + [ConfigurationProperty(VALUE_KEY, IsRequired = true)] + public string Value + { + get + { + return (string)base[VALUE_KEY]; + } + } + } +} diff --git a/src/Umbraco.Core/Configuration/HealthChecks/NotificationMethodSettingsElementCollection.cs b/src/Umbraco.Core/Configuration/HealthChecks/NotificationMethodSettingsElementCollection.cs new file mode 100644 index 0000000000..e4ee7b6274 --- /dev/null +++ b/src/Umbraco.Core/Configuration/HealthChecks/NotificationMethodSettingsElementCollection.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Configuration; + +namespace Umbraco.Core.Configuration.HealthChecks +{ + [ConfigurationCollection(typeof(NotificationMethodSettingsElement), AddItemName = "add")] + public class NotificationMethodSettingsElementCollection : ConfigurationElementCollection, IEnumerable + { + protected override ConfigurationElement CreateNewElement() + { + return new NotificationMethodSettingsElement(); + } + + protected override object GetElementKey(ConfigurationElement element) + { + return ((NotificationMethodSettingsElement)(element)).Key; + } + + new public NotificationMethodSettingsElement this[string key] + { + get + { + return (NotificationMethodSettingsElement)BaseGet(key); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + for (var i = 0; i < Count; i++) + { + yield return BaseGet(i) as NotificationMethodSettingsElement; + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/src/Umbraco.Core/Configuration/HealthChecks/NotificationMethodsElementCollection.cs b/src/Umbraco.Core/Configuration/HealthChecks/NotificationMethodsElementCollection.cs new file mode 100644 index 0000000000..79cf6a2933 --- /dev/null +++ b/src/Umbraco.Core/Configuration/HealthChecks/NotificationMethodsElementCollection.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Configuration; + +namespace Umbraco.Core.Configuration.HealthChecks +{ + [ConfigurationCollection(typeof(NotificationMethodElement), AddItemName = "notificationMethod")] + public class NotificationMethodsElementCollection : ConfigurationElementCollection, IEnumerable + { + protected override ConfigurationElement CreateNewElement() + { + return new NotificationMethodElement(); + } + + protected override object GetElementKey(ConfigurationElement element) + { + return ((NotificationMethodElement)(element)).Alias; + } + + new public NotificationMethodElement this[string key] + { + get + { + return (NotificationMethodElement)BaseGet(key); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + for (var i = 0; i < Count; i++) + { + yield return BaseGet(i) as NotificationMethodElement; + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/src/Umbraco.Core/Configuration/HealthChecks/SlackSettingsElement.cs b/src/Umbraco.Core/Configuration/HealthChecks/SlackSettingsElement.cs deleted file mode 100644 index 0dbf6673cc..0000000000 --- a/src/Umbraco.Core/Configuration/HealthChecks/SlackSettingsElement.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Configuration; - -namespace Umbraco.Core.Configuration.HealthChecks -{ - public class SlackSettingsElement : BaseNotificationMethodElement - { - private const string WEBHOOKURL_KEY = "webHookUrl"; - private const string CHANNEL_KEY = "channel"; - private const string USERNAME_KEY = "username"; - - [ConfigurationProperty(WEBHOOKURL_KEY, IsRequired = true)] - public string WebHookUrl - { - get - { - return ((string)(base[WEBHOOKURL_KEY])); - } - } - - [ConfigurationProperty(CHANNEL_KEY, IsRequired = true)] - public string Channel - { - get - { - return ((string)(base[CHANNEL_KEY])); - } - } - - [ConfigurationProperty(USERNAME_KEY, IsRequired = true)] - public string UserName - { - get - { - return ((string)(base[USERNAME_KEY])); - } - } - } -} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 0e0f9235db..be8ce272dd 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -218,11 +218,12 @@ - + + + + - - diff --git a/src/Umbraco.Web.UI/config/HealthChecks.config b/src/Umbraco.Web.UI/config/HealthChecks.config index a4c4d374fb..0b3bfe6a5a 100644 --- a/src/Umbraco.Web.UI/config/HealthChecks.config +++ b/src/Umbraco.Web.UI/config/HealthChecks.config @@ -11,9 +11,22 @@ https://our.umbraco.org/documentation/reference/config/healthchecks - - - + + + + + + + + + + + + + + + + diff --git a/src/Umbraco.Web/HealthCheck/HealthCheckNotificationMethodAttribute.cs b/src/Umbraco.Web/HealthCheck/HealthCheckNotificationMethodAttribute.cs new file mode 100644 index 0000000000..0b09e67839 --- /dev/null +++ b/src/Umbraco.Web/HealthCheck/HealthCheckNotificationMethodAttribute.cs @@ -0,0 +1,18 @@ +using System; + +namespace Umbraco.Web.HealthCheck +{ + /// + /// Metadata attribute for health check notification methods + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public sealed class HealthCheckNotificationMethodAttribute : Attribute + { + public HealthCheckNotificationMethodAttribute(string alias) + { + Alias = alias; + } + + public string Alias { get; private set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/HealthCheck/HealthCheckNotificationMethodResolver.cs b/src/Umbraco.Web/HealthCheck/HealthCheckNotificationMethodResolver.cs new file mode 100644 index 0000000000..ca17dcdcd8 --- /dev/null +++ b/src/Umbraco.Web/HealthCheck/HealthCheckNotificationMethodResolver.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.ObjectResolution; +using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Web.HealthCheck.NotificationMethods; + +namespace Umbraco.Web.HealthCheck +{ + using System.Configuration; + using System.Reflection; + + using Umbraco.Core.Configuration.HealthChecks; + + /// + /// Resolves all health check instances + /// + /// + /// Each instance scoped to the lifespan of the http request + /// + internal class HealthCheckNotificationMethodResolver : LazyManyObjectsResolverBase, IHealthCheckNotificationMethodsResolver + { + public HealthCheckNotificationMethodResolver(ILogger logger, Func> lazyTypeList) + : base(new HealthCheckNotificationMethodServiceProvider(), logger, lazyTypeList, ObjectLifetimeScope.Application) + { + } + + /// + /// Returns all health check notification method instances + /// + public IEnumerable NotificationMethods + { + get { return Values; } + } + + /// + /// This will ctor the IHealthCheckNotificatationMethod instances + /// + private class HealthCheckNotificationMethodServiceProvider : IServiceProvider + { + public object GetService(Type serviceType) + { + var ctor = serviceType.GetConstructors().FirstOrDefault(); + if (ctor == null) + { + return null; + } + + // Load attribute from type in order to find alias for notification method + var attribute = serviceType.GetCustomAttributes(typeof(HealthCheckNotificationMethodAttribute), true) + .FirstOrDefault() as HealthCheckNotificationMethodAttribute; + if (attribute == null) + { + return null; + } + + // Using alias, get related configuration + var healthCheckConfig = (HealthChecksSection)ConfigurationManager.GetSection("umbracoConfiguration/HealthChecks"); + var notificationMethods = healthCheckConfig.NotificationSettings.NotificationMethods; + var notificationMethod = notificationMethods[attribute.Alias]; + if (notificationMethod == null) + { + return null; + } + + // Create array for constructor paramenters. Will consists of common ones that all notification methods have as well + // as those specific to this particular notification method. + var baseType = typeof(NotificationMethodBase); + var baseTypeCtor = baseType.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).First(); + var baseTypeCtorParamNames = baseTypeCtor.GetParameters().Select(x => x.Name); + var ctorParams = new List { notificationMethod.Enabled, notificationMethod.FailureOnly, notificationMethod.Verbosity }; + ctorParams.AddRange(ctor.GetParameters() + .Where(x => baseTypeCtorParamNames.Contains(x.Name) == false) + .Select(x => notificationMethod.Settings[x.Name].Value)); + + // Instantiate the type with the constructor parameters + return Activator.CreateInstance(serviceType, ctorParams.ToArray()); + } + } + } +} diff --git a/src/Umbraco.Web/HealthCheck/HealthCheckResults.cs b/src/Umbraco.Web/HealthCheck/HealthCheckResults.cs index a334e9dbb8..4d4ec832d8 100644 --- a/src/Umbraco.Web/HealthCheck/HealthCheckResults.cs +++ b/src/Umbraco.Web/HealthCheck/HealthCheckResults.cs @@ -8,12 +8,12 @@ using Umbraco.Core.Logging; namespace Umbraco.Web.HealthCheck { - internal class HealthCheckResults + public class HealthCheckResults { private readonly Dictionary> _results; internal readonly bool AllChecksSuccessful; - internal HealthCheckResults(IEnumerable checks) + public HealthCheckResults(IEnumerable checks) { _results = checks.ToDictionary( t => t.Name, @@ -49,7 +49,7 @@ namespace Umbraco.Web.HealthCheck } } - internal void LogResults() + public void LogResults() { LogHelper.Info("Scheduled health check results:"); foreach (var result in _results) @@ -73,7 +73,7 @@ namespace Umbraco.Web.HealthCheck } } - internal string ResultsAsMarkDown(HealthCheckNotificationVerbosity verbosity, bool slackMarkDown = false) + public string ResultsAsMarkDown(HealthCheckNotificationVerbosity verbosity, bool slackMarkDown = false) { var newItem = "- "; if (slackMarkDown) @@ -121,7 +121,7 @@ namespace Umbraco.Web.HealthCheck return sb.ToString(); } - internal string ResultsAsHtml(HealthCheckNotificationVerbosity verbosity) + public string ResultsAsHtml(HealthCheckNotificationVerbosity verbosity) { var mark = new Markdown(); var html = mark.Transform(ResultsAsMarkDown(verbosity)); @@ -151,6 +151,7 @@ namespace Umbraco.Web.HealthCheck .Replace("", "_") .Replace("", "_"); } + return html.Replace("", "**") .Replace("", "**") .Replace("", "*") diff --git a/src/Umbraco.Web/HealthCheck/IHealthCheckNotificationMethodsResolver.cs b/src/Umbraco.Web/HealthCheck/IHealthCheckNotificationMethodsResolver.cs new file mode 100644 index 0000000000..21bdc7fcb4 --- /dev/null +++ b/src/Umbraco.Web/HealthCheck/IHealthCheckNotificationMethodsResolver.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using Umbraco.Web.HealthCheck.NotificationMethods; + +namespace Umbraco.Web.HealthCheck +{ + public interface IHealthCheckNotificationMethodsResolver + { + IEnumerable NotificationMethods { get; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/HealthCheck/NotificationMethods/EmailNotificationMethod.cs b/src/Umbraco.Web/HealthCheck/NotificationMethods/EmailNotificationMethod.cs new file mode 100644 index 0000000000..ceca0f371b --- /dev/null +++ b/src/Umbraco.Web/HealthCheck/NotificationMethods/EmailNotificationMethod.cs @@ -0,0 +1,53 @@ +using System; +using System.Net.Mail; +using System.Threading.Tasks; +using Umbraco.Core.Configuration.HealthChecks; + +namespace Umbraco.Web.HealthCheck.NotificationMethods +{ + [HealthCheckNotificationMethod("email")] + public class EmailNotificationMethod : NotificationMethodBase, IHealthCheckNotificatationMethod + { + public EmailNotificationMethod(bool enabled, bool failureOnly, HealthCheckNotificationVerbosity verbosity, + string recipientEmail, string subject) + : base(enabled, failureOnly, verbosity) + { + RecipientEmail = recipientEmail; + Subject = subject; + Verbosity = verbosity; + } + + public string RecipientEmail { get; set; } + + public string Subject { get; set; } + + public async Task SendAsync(HealthCheckResults results) + { + if (ShouldSend(results) == false) + { + return; + } + + if (string.IsNullOrEmpty(RecipientEmail)) + { + return; + } + + using (var client = new SmtpClient()) + using (var mailMessage = new MailMessage()) + { + mailMessage.To.Add(RecipientEmail); + mailMessage.Body = + string.Format( + "

Results of the scheduled Umbraco Health Checks run on {0} at {1} are as follows:

{2}", + DateTime.Now.ToShortDateString(), + DateTime.Now.ToShortTimeString(), + results.ResultsAsHtml(Verbosity)); + mailMessage.Subject = string.IsNullOrEmpty(Subject) ? "Umbraco Health Check Status" : Subject; + mailMessage.IsBodyHtml = true; + + await client.SendMailAsync(mailMessage); + } + } + } +} diff --git a/src/Umbraco.Web/HealthCheck/NotificationMethods/IHealthCheckNotificatationMethod.cs b/src/Umbraco.Web/HealthCheck/NotificationMethods/IHealthCheckNotificatationMethod.cs new file mode 100644 index 0000000000..1b7952c111 --- /dev/null +++ b/src/Umbraco.Web/HealthCheck/NotificationMethods/IHealthCheckNotificatationMethod.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Umbraco.Web.HealthCheck.NotificationMethods +{ + public interface IHealthCheckNotificatationMethod + { + Task SendAsync(HealthCheckResults results); + } +} diff --git a/src/Umbraco.Web/HealthCheck/NotificationMethods/NotificationMethodBase.cs b/src/Umbraco.Web/HealthCheck/NotificationMethods/NotificationMethodBase.cs new file mode 100644 index 0000000000..763d732c21 --- /dev/null +++ b/src/Umbraco.Web/HealthCheck/NotificationMethods/NotificationMethodBase.cs @@ -0,0 +1,35 @@ +using Umbraco.Core.Configuration.HealthChecks; + +namespace Umbraco.Web.HealthCheck.NotificationMethods +{ + public abstract class NotificationMethodBase + { + protected NotificationMethodBase(bool enabled, bool failureOnly, HealthCheckNotificationVerbosity verbosity) + { + Enabled = enabled; + FailureOnly = failureOnly; + Verbosity = verbosity; + } + + public bool Enabled { get; set; } + + public bool FailureOnly { get; set; } + + public HealthCheckNotificationVerbosity Verbosity { get; set; } + + protected bool ShouldSend(HealthCheckResults results) + { + if (Enabled == false) + { + return false; + } + + if (FailureOnly && results.AllChecksSuccessful) + { + return false; + } + + return true; + } + } +} diff --git a/src/Umbraco.Web/HealthCheck/NotificationMethods/SlackNotificationMethod.cs b/src/Umbraco.Web/HealthCheck/NotificationMethods/SlackNotificationMethod.cs new file mode 100644 index 0000000000..7368a41ba0 --- /dev/null +++ b/src/Umbraco.Web/HealthCheck/NotificationMethods/SlackNotificationMethod.cs @@ -0,0 +1,55 @@ +using System.Threading.Tasks; +using Slack.Webhooks; +using Umbraco.Core.Configuration.HealthChecks; + +namespace Umbraco.Web.HealthCheck.NotificationMethods +{ + [HealthCheckNotificationMethod("slack")] + public class SlackNotificationMethod : NotificationMethodBase, IHealthCheckNotificatationMethod + { + public SlackNotificationMethod(bool enabled, bool failureOnly, HealthCheckNotificationVerbosity verbosity, + string webHookUrl, string channel, string username) + : base(enabled, failureOnly, verbosity) + { + WebHookUrl = webHookUrl; + Channel = channel; + Username = username; + } + + public string WebHookUrl { get; set; } + + public string Channel { get; set; } + + public string Username { get; set; } + + public async Task SendAsync(HealthCheckResults results) + { + if (ShouldSend(results) == false) + { + return; + } + + if (string.IsNullOrEmpty(WebHookUrl) || string.IsNullOrEmpty(Channel) || string.IsNullOrEmpty(Username)) + { + return; + } + + var slackClient = new SlackClient(WebHookUrl); + + var icon = Emoji.Warning; + if (results.AllChecksSuccessful) + { + icon = Emoji.WhiteCheckMark; + } + + var slackMessage = new SlackMessage + { + Channel = Channel, + Text = results.ResultsAsMarkDown(Verbosity, true), + IconEmoji = icon, + Username = Username + }; + await slackClient.PostAsync(slackMessage); + } + } +} diff --git a/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs b/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs index 62dd98136c..a9002c61dd 100644 --- a/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs +++ b/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs @@ -69,47 +69,11 @@ namespace Umbraco.Web.Scheduling var results = new HealthCheckResults(checks); results.LogResults(); - // Send to email address if configured observing if the configuration is set to only notify if there are any failures - var emailNotificationSettings = healthCheckConfig.NotificationSettings.EmailSettings; - if (emailNotificationSettings != null && string.IsNullOrEmpty(emailNotificationSettings.RecipientEmail) == false - && (emailNotificationSettings.FailureOnly == false || emailNotificationSettings.FailureOnly && results.AllChecksSuccessful == false)) + // Send using registered notification methods + var registeredNotificationMethods = HealthCheckNotificationMethodResolver.Current.NotificationMethods; + foreach (var notificationMethod in registeredNotificationMethods) { - using (var client = new SmtpClient()) - using (var mailMessage = new MailMessage()) - { - mailMessage.To.Add(emailNotificationSettings.RecipientEmail); - mailMessage.Body = - string.Format( - "

Results of the scheduled Umbraco Health Checks run on {0} at {1} are as follows:

{2}", - DateTime.Now.ToShortDateString(), DateTime.Now.ToShortTimeString(), - results.ResultsAsHtml(emailNotificationSettings.Verbosity)); - mailMessage.Subject = emailNotificationSettings.Subject; - mailMessage.IsBodyHtml = true; - - await client.SendMailAsync(mailMessage); - } - } - - // Send Slack incoming webhook if configured observing if the configuration is set to only notify if there are any failures - var slackNotificationSettings = healthCheckConfig.NotificationSettings.SlackSettings; - if (slackNotificationSettings != null && string.IsNullOrEmpty(slackNotificationSettings.WebHookUrl) == false && (slackNotificationSettings.FailureOnly == false || slackNotificationSettings.FailureOnly && results.AllChecksSuccessful == false)) - { - var slackClient = new SlackClient(slackNotificationSettings.WebHookUrl); - - var icon = Emoji.Warning; - if (results.AllChecksSuccessful) - { - icon = Emoji.WhiteCheckMark; - } - - var slackMessage = new SlackMessage - { - Channel = slackNotificationSettings.Channel, - Text = results.ResultsAsMarkDown(slackNotificationSettings.Verbosity, true), - IconEmoji = icon, - Username = slackNotificationSettings.UserName - }; - slackClient.Post(slackMessage); + await notificationMethod.SendAsync(results); } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index ca2ec136fd..a611607db3 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -362,16 +362,23 @@ + + + + + + + diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs index 29565065e2..debfd8ccea 100644 --- a/src/Umbraco.Web/WebBootManager.cs +++ b/src/Umbraco.Web/WebBootManager.cs @@ -544,6 +544,8 @@ namespace Umbraco.Web HealthCheckResolver.Current = new HealthCheckResolver(LoggerResolver.Current.Logger, () => PluginManager.ResolveTypes()); + HealthCheckNotificationMethodResolver.Current = new HealthCheckNotificationMethodResolver(LoggerResolver.Current.Logger, + () => PluginManager.ResolveTypes()); } ///