From 018d851c701a8ca12ccd6696f1ceb3d8b4cc8aad Mon Sep 17 00:00:00 2001 From: Joshua Holt Date: Wed, 3 Feb 2021 10:25:01 -0600 Subject: [PATCH] Allow configuration of CronExpressionDescriptor options --- sample/Startup.cs | 11 +++- .../Controllers/TriggersController.cs | 5 +- src/SilkierQuartz/Extensions.cs | 5 +- src/SilkierQuartz/SilkierQuartzOptions.cs | 7 ++- test/ExpressionDescriptorUnitTests.cs | 54 +++++++++++++++++++ 5 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 test/ExpressionDescriptorUnitTests.cs diff --git a/sample/Startup.cs b/sample/Startup.cs index 7fa3965..5c396fc 100644 --- a/sample/Startup.cs +++ b/sample/Startup.cs @@ -59,7 +59,11 @@ namespace SilkierQuartz.Example VirtualPathRoot = "/SilkierQuartz", UseLocalTime = true, DefaultDateFormat = "yyyy-MM-dd", - DefaultTimeFormat = "HH:mm:ss" + DefaultTimeFormat = "HH:mm:ss", + CronExpressionOptions = new CronExpressionDescriptor.Options() + { + DayOfWeekStartIndexZero = false //Quartz uses 1-7 as the range + } } ); app.UseEndpoints(endpoints => @@ -86,7 +90,10 @@ namespace SilkierQuartz.Example TriggerBuilder.Create() .WithSimpleSchedule(x => x.WithIntervalInSeconds(1).RepeatForever()), TriggerBuilder.Create() - .WithSimpleSchedule(x => x.WithIntervalInSeconds(2).RepeatForever()) + .WithSimpleSchedule(x => x.WithIntervalInSeconds(2).RepeatForever()), + //Add a sample that uses 1-7 for dow + TriggerBuilder.Create() + .WithCronSchedule("0 0 2 ? * 7 *"), }); app.UseQuartzJob(() => diff --git a/src/SilkierQuartz/Controllers/TriggersController.cs b/src/SilkierQuartz/Controllers/TriggersController.cs index 48bfab0..cc5cb9e 100644 --- a/src/SilkierQuartz/Controllers/TriggersController.cs +++ b/src/SilkierQuartz/Controllers/TriggersController.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using CronExpressionDescriptor; #region Target-Specific Directives #if ( NETSTANDARD || NETCOREAPP ) @@ -42,7 +43,7 @@ namespace SilkierQuartz.Controllers JobKey = t.JobKey.ToString(), JobGroup = t.JobKey.Group, JobName = t.JobKey.Name, - ScheduleDescription = t.GetScheduleDescription(), + ScheduleDescription = t.GetScheduleDescription(Services), History = Histogram.Empty, StartTime = t.StartTimeUtc.UtcDateTime.ToDefaultFormat(), EndTime = t.FinalFireTimeUtc?.UtcDateTime.ToDefaultFormat(), @@ -247,7 +248,7 @@ namespace SilkierQuartz.Controllers try { - desc = CronExpressionDescriptor.ExpressionDescriptor.GetDescription(cron); + desc = CronExpressionDescriptor.ExpressionDescriptor.GetDescription(cron, Services.Options?.CronExpressionOptions); } catch { } diff --git a/src/SilkierQuartz/Extensions.cs b/src/SilkierQuartz/Extensions.cs index 5509825..33d1135 100644 --- a/src/SilkierQuartz/Extensions.cs +++ b/src/SilkierQuartz/Extensions.cs @@ -9,6 +9,7 @@ using System.Reflection; using System.Text; using System.Threading.Tasks; using System.IO; +using CronExpressionDescriptor; using Quartz.Impl.Matchers; using Quartz.Plugins.RecentHistory; @@ -293,10 +294,10 @@ namespace SilkierQuartz return TriggerType.Unknown; } - public static string GetScheduleDescription(this ITrigger trigger) + public static string GetScheduleDescription(this ITrigger trigger, Services services) { if (trigger is ICronTrigger cr) - return CronExpressionDescriptor.ExpressionDescriptor.GetDescription(cr.CronExpressionString); + return CronExpressionDescriptor.ExpressionDescriptor.GetDescription(cr.CronExpressionString, services?.Options?.CronExpressionOptions); if (trigger is IDailyTimeIntervalTrigger dt) return GetScheduleDescription(dt); if (trigger is ISimpleTrigger st) diff --git a/src/SilkierQuartz/SilkierQuartzOptions.cs b/src/SilkierQuartz/SilkierQuartzOptions.cs index 4cc9537..a744c9d 100644 --- a/src/SilkierQuartz/SilkierQuartzOptions.cs +++ b/src/SilkierQuartz/SilkierQuartzOptions.cs @@ -3,7 +3,7 @@ using Quartz.Impl; using SilkierQuartz.TypeHandlers; using System.Collections.Generic; using System.IO; - +using CronExpressionDescriptor; using Number = SilkierQuartz.TypeHandlers.NumberHandler.UnderlyingType; namespace SilkierQuartz @@ -51,6 +51,11 @@ namespace SilkierQuartz set => DateTimeSettings.UseLocalTime = value; } + /// + /// Set options used by the cron expression description provider + /// + public Options CronExpressionOptions { get; set; } = new Options(); + public SilkierQuartzOptions() { DefaultSelectedType = new StringHandler() { Name = "String" }; diff --git a/test/ExpressionDescriptorUnitTests.cs b/test/ExpressionDescriptorUnitTests.cs new file mode 100644 index 0000000..454510f --- /dev/null +++ b/test/ExpressionDescriptorUnitTests.cs @@ -0,0 +1,54 @@ +using System; +using CronExpressionDescriptor; +using FluentAssertions; +using Quartz; +using Xunit; + +namespace SilkierQuartz.Test +{ + //7 is used as Sunday on some systems, and considered valid. + //Quartz uses this by default, and SilkierQuartz should also + + //Allowed value range in comments; + //https://github.com/quartznet/quartznet/blob/a22915a9abac1568accb93eb24b4cce5331c8249/src/Quartz/CronExpression.cs#L91 + + public class ExpressionDescriptorUnitTests + { + public static Options ZeroBaseDoWIdxOptions = new Options() {DayOfWeekStartIndexZero = true}; + public static readonly Options OneBasedDoWIdxOptions = new Options() {DayOfWeekStartIndexZero = false}; + + [Theory(DisplayName = "Parse Expressions")] + [InlineData("0 0 2 ? * 7 *", "At 02:00, only on Saturday", false)] + [InlineData("0 0 7 * * ?", "At 07:00", false)] + [InlineData("0 0 20 * * ?", "At 20:00", false)] + [InlineData("0 0 20 6 1/1 ? *", "At 20:00, on day 6 of the month", false)] + [InlineData("0 0 19 20 11 ?", "At 19:00, on day 20 of the month, only in November", false)] + [InlineData("0 10,15,20 12 ? * 6,7 *", "At 10, 15, and 20 minutes past the hour, at 12:00, only on Friday and Saturday", false)] + [InlineData("0 30 10-13 ? * FRI#3", "At 30 minutes past the hour, between 10:00 and 13:59, on the third Friday of the month", false)] + [InlineData("0 43 9 ? * 5L", "At 09:43, on the last Thursday of the month", false)] + + [InlineData("0 0 2 ? * 6 *", "At 02:00, only on Saturday", true)] + [InlineData("0 0 7 * * ?", "At 07:00", true)] + [InlineData("0 0 20 * * ?", "At 20:00", true)] + [InlineData("0 0 20 6 1/1 ? *", "At 20:00, on day 6 of the month", true)] + [InlineData("0 0 19 20 11 ?", "At 19:00, on day 20 of the month, only in November", true)] + [InlineData("0 10,15,20 12 ? * 5,6 *", "At 10, 15, and 20 minutes past the hour, at 12:00, only on Friday and Saturday", true)] + [InlineData("0 30 10-13 ? * FRI#3", "At 30 minutes past the hour, between 10:00 and 13:59, on the third Friday of the month", true)] + [InlineData("0 43 9 ? * 4L", "At 09:43, on the last Thursday of the month", true)] + public void ShouldParseExpressions(string cron, string expected, bool isZeroBased) + { + var options = new Options() {DayOfWeekStartIndexZero = isZeroBased}; + CronExpression exp = null; + //Ensure quartz properly parses the cron + var ex = Record.Exception(() => exp = new CronExpression(cron)); + + ex.Should().BeNull("Quartz should correctly parse any expression before we can expect a valid description"); + + var result = ExpressionDescriptor.GetDescription(cron, options); + result.Should() + .NotBeNull(); + + result.Should().Be(expected); + } + } +} \ No newline at end of file