Changed configuration of first run time for health check notifier from a time string to a cron expression.
This commit is contained in:
@@ -42,11 +42,11 @@ namespace Umbraco.Core.Configuration.Models.Validation
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ValidateOptionalTime(string configPath, string value, out string message)
|
||||
public bool ValidateOptionalCronTab(string configPath, string value, out string message)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(value) && !value.IsValidTimeSpan())
|
||||
if (!string.IsNullOrEmpty(value) && !value.IsValidCronTab())
|
||||
{
|
||||
message = $"Configuration entry {configPath} contains an invalid time value.";
|
||||
message = $"Configuration entry {configPath} contains an invalid cron expression.";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Umbraco.Core.Configuration.Models.Validation
|
||||
|
||||
private bool ValidateNotificationFirstRunTime(string value, out string message)
|
||||
{
|
||||
return ValidateOptionalTime($"{Constants.Configuration.ConfigHealthChecks}:{nameof(HealthChecksSettings.Notification)}:{nameof(HealthChecksSettings.Notification.FirstRunTime)}", value, out message);
|
||||
return ValidateOptionalCronTab($"{Constants.Configuration.ConfigHealthChecks}:{nameof(HealthChecksSettings.Notification)}:{nameof(HealthChecksSettings.Notification.FirstRunTime)}", value, out message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,42 +43,5 @@ namespace Umbraco.Core
|
||||
Minute,
|
||||
Second
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the number of minutes from a date time, on a rolling daily basis (so if
|
||||
/// date time is before the time, calculate onto next day).
|
||||
/// </summary>
|
||||
/// <param name="fromDateTime">Date to start from</param>
|
||||
/// <param name="scheduledTime">Time to compare against (in Hmm form, e.g. 330, 2200)</param>
|
||||
/// <returns></returns>
|
||||
public static int PeriodicMinutesFrom(this DateTime fromDateTime, string scheduledTime)
|
||||
{
|
||||
// Ensure time provided is 4 digits long
|
||||
if (scheduledTime.Length == 3)
|
||||
{
|
||||
scheduledTime = "0" + scheduledTime;
|
||||
}
|
||||
|
||||
var scheduledHour = int.Parse(scheduledTime.Substring(0, 2));
|
||||
var scheduledMinute = int.Parse(scheduledTime.Substring(2));
|
||||
|
||||
DateTime scheduledDateTime;
|
||||
if (IsScheduledInRemainingDay(fromDateTime, scheduledHour, scheduledMinute))
|
||||
{
|
||||
scheduledDateTime = new DateTime(fromDateTime.Year, fromDateTime.Month, fromDateTime.Day, scheduledHour, scheduledMinute, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
var nextDay = fromDateTime.AddDays(1);
|
||||
scheduledDateTime = new DateTime(nextDay.Year, nextDay.Month, nextDay.Day, scheduledHour, scheduledMinute, 0);
|
||||
}
|
||||
|
||||
return (int)(scheduledDateTime - fromDateTime).TotalMinutes;
|
||||
}
|
||||
|
||||
private static bool IsScheduledInRemainingDay(DateTime fromDateTime, int scheduledHour, int scheduledMinute)
|
||||
{
|
||||
return scheduledHour > fromDateTime.Hour || (scheduledHour == fromDateTime.Hour && scheduledMinute >= fromDateTime.Minute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1480,18 +1480,23 @@ namespace Umbraco.Core
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates a string matches a time stamp.
|
||||
/// Validates a string matches a cron tab (for length only).
|
||||
/// </summary>
|
||||
/// <param name="input">String with timespan representation (in standard timespan format: https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-timespan-format-strings)</param>
|
||||
/// <returns></returns>
|
||||
public static bool IsValidTimeSpan(this string input)
|
||||
/// <param name="input">String with timespan representation (in cron tab format: https://github.com/atifaziz/NCrontab/wiki/Crontab-Expression)</param>
|
||||
/// <returns>True if string matches a valid cron expression, false if not.</returns>
|
||||
/// <remarks>
|
||||
/// Considering an expression as valid if it's supported by https://github.com/atifaziz/NCrontab/wiki/Crontab-Expression,
|
||||
/// so only 5 or 6 values are expected.
|
||||
/// </remarks>
|
||||
public static bool IsValidCronTab(this string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return TimeSpan.TryParse(input, out var _);
|
||||
var parts = input.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
|
||||
return parts.Length == 5 || parts.Length == 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using NCrontab;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Models.Extensions
|
||||
namespace Umbraco.Infrastructure.Configuration.Extensions
|
||||
{
|
||||
public static class HealthCheckSettingsExtensions
|
||||
{
|
||||
@@ -14,9 +16,11 @@ namespace Umbraco.Core.Configuration.Models.Extensions
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise start at scheduled time.
|
||||
var delay = TimeSpan.FromMinutes(now.PeriodicMinutesFrom(firstRunTime));
|
||||
return (delay < defaultDelay)
|
||||
// Otherwise start at scheduled time according to cron expression, unless within the default delay period.
|
||||
var firstRunTimeCronExpression = CrontabSchedule.Parse(firstRunTime);
|
||||
var firstRunOccurance = firstRunTimeCronExpression.GetNextOccurrence(now);
|
||||
var delay = firstRunOccurance - now;
|
||||
return delay < defaultDelay
|
||||
? defaultDelay
|
||||
: delay;
|
||||
}
|
||||
@@ -4,11 +4,11 @@ using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Configuration.Models.Extensions;
|
||||
using Umbraco.Core.HealthCheck;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Core.Sync;
|
||||
using Umbraco.Infrastructure.Configuration.Extensions;
|
||||
using Umbraco.Infrastructure.HealthCheck;
|
||||
using Umbraco.Web.HealthCheck;
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="3.1.8" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" />
|
||||
<PackageReference Include="MiniProfiler.Shared" Version="4.2.1" />
|
||||
<PackageReference Include="ncrontab" Version="3.3.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="NPoco" Version="4.0.2" />
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Configuration.Models.Extensions;
|
||||
using Umbraco.Infrastructure.Configuration.Extensions;
|
||||
|
||||
namespace Umbraco.Tests.UnitTests.Umbraco.Core.Configuration.Models.Extensions
|
||||
{
|
||||
[TestFixture]
|
||||
public class HealthCheckSettingsExtensionsTests
|
||||
{
|
||||
[Test]
|
||||
public void Returns_Notification_Delay_From_Provided_Time()
|
||||
[TestCase("30 12 * * *", 30)]
|
||||
[TestCase("15 18 * * *", 60 * 6 + 15)]
|
||||
[TestCase("0 3 * * *", 60 * 15)]
|
||||
[TestCase("0 3 2 * *", 24 * 60 * 1 + 60 * 15)]
|
||||
[TestCase("0 6 * * 3", 24 * 60 * 3 + 60 * 18)]
|
||||
public void Returns_Notification_Delay_From_Provided_Time(string firstRunTimeCronExpression, int expectedDelayInMinutes)
|
||||
{
|
||||
var settings = new HealthChecksSettings
|
||||
{
|
||||
Notification = new HealthChecksNotificationSettings
|
||||
{
|
||||
FirstRunTime = "1230",
|
||||
FirstRunTime = firstRunTimeCronExpression,
|
||||
}
|
||||
};
|
||||
var now = DateTime.Now.Date.AddHours(12);
|
||||
var now = new DateTime(2020, 10, 31, 12, 0, 0);
|
||||
var result = settings.GetNotificationDelay(now, TimeSpan.Zero);
|
||||
Assert.AreEqual(30, result.Minutes);
|
||||
Assert.AreEqual(expectedDelayInMinutes, result.TotalMinutes);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -30,12 +34,12 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Configuration.Models.Extensions
|
||||
{
|
||||
Notification = new HealthChecksNotificationSettings
|
||||
{
|
||||
FirstRunTime = "1230",
|
||||
FirstRunTime = "30 12 * * *",
|
||||
}
|
||||
};
|
||||
var now = DateTime.Now.Date.AddHours(12).AddMinutes(25);
|
||||
var now = new DateTime(2020, 10, 31, 12, 25, 0);
|
||||
var result = settings.GetNotificationDelay(now, TimeSpan.FromMinutes(10));
|
||||
Assert.AreEqual(10, result.Minutes);
|
||||
Assert.AreEqual(10, result.TotalMinutes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Tests.UnitTests.Umbraco.Core
|
||||
{
|
||||
[TestFixture]
|
||||
public class DateTimeExtensionsTests
|
||||
{
|
||||
[Test]
|
||||
public void PeriodicMinutesFrom_PostTime_CalculatesMinutesBetween()
|
||||
{
|
||||
var nowDateTime = new DateTime(2017, 1, 1, 10, 30, 0);
|
||||
var scheduledTime = "1145";
|
||||
var minutesBetween = nowDateTime.PeriodicMinutesFrom(scheduledTime);
|
||||
Assert.AreEqual(75, minutesBetween);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PeriodicMinutesFrom_PriorTime_CalculatesMinutesBetween()
|
||||
{
|
||||
var nowDateTime = new DateTime(2017, 1, 1, 10, 30, 0);
|
||||
var scheduledTime = "900";
|
||||
var minutesBetween = nowDateTime.PeriodicMinutesFrom(scheduledTime);
|
||||
Assert.AreEqual(1350, minutesBetween);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PeriodicMinutesFrom_PriorTime_WithLeadingZero_CalculatesMinutesBetween()
|
||||
{
|
||||
var nowDateTime = new DateTime(2017, 1, 1, 10, 30, 0);
|
||||
var scheduledTime = "0900";
|
||||
var minutesBetween = nowDateTime.PeriodicMinutesFrom(scheduledTime);
|
||||
Assert.AreEqual(1350, minutesBetween);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PeriodicMinutesFrom_SameTime_CalculatesMinutesBetween()
|
||||
{
|
||||
var nowDateTime = new DateTime(2017, 1, 1, 10, 30, 0);
|
||||
var scheduledTime = "1030";
|
||||
var minutesBetween = nowDateTime.PeriodicMinutesFrom(scheduledTime);
|
||||
Assert.AreEqual(0, minutesBetween);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -302,13 +302,12 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.ShortStringHelper
|
||||
}
|
||||
|
||||
[TestCase("", false)]
|
||||
[TestCase("12:34", true)]
|
||||
[TestCase("1:14:23", true)]
|
||||
[TestCase("25:03", false)]
|
||||
[TestCase("18:61", false)]
|
||||
public void IsValidTimeSpan(string input, bool expected)
|
||||
[TestCase("* * * * 1", true)]
|
||||
[TestCase("* * * * * 1", true)]
|
||||
[TestCase("* * * 1", false)]
|
||||
public void IsValidCronTab(string input, bool expected)
|
||||
{
|
||||
var result = input.IsValidTimeSpan();
|
||||
var result = input.IsValidCronTab();
|
||||
Assert.AreEqual(expected, result);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user