From 676ae9ef75db977dc5e863b46c5016dfd785dee0 Mon Sep 17 00:00:00 2001 From: Thomas Holliday Date: Thu, 2 Nov 2023 06:03:09 +1000 Subject: [PATCH] Pulling timezoneInfo from TimezoneId provided in config as well as nested cron timer check when the config value for the job name is an object instead of a cron string. --- .../IRecurringJobManagerExtensions.cs | 8 +-- .../MIFCoreRecurringJobManager.cs | 70 +++++++++++++++---- MIFCore.TestApp/MIFCore.TestApp.csproj | 4 +- MIFCore.TestApp/SomeJob.cs | 9 +++ MIFCore.TestApp/Startup.cs | 11 ++- MIFCore.TestApp/settings.json | 16 +++-- 6 files changed, 95 insertions(+), 23 deletions(-) diff --git a/MIFCore.Hangfire/IRecurringJobManagerExtensions.cs b/MIFCore.Hangfire/IRecurringJobManagerExtensions.cs index fe849a3..3204d54 100644 --- a/MIFCore.Hangfire/IRecurringJobManagerExtensions.cs +++ b/MIFCore.Hangfire/IRecurringJobManagerExtensions.cs @@ -7,24 +7,24 @@ namespace MIFCore.Hangfire { public static class IRecurringJobManagerExtensions { - public static void CreateRecurringJob(this IRecurringJobManager recurringJobManager, string jobName, Expression> methodCall, string cronSchedule = null, string queue = "default", bool triggerIfNeverExecuted = false) + public static void CreateRecurringJob(this IRecurringJobManager recurringJobManager, string jobName, Expression> methodCall, string cronSchedule = null, string queue = "default", bool triggerIfNeverExecuted = false, TimeZoneInfo timeZone = null) { var manager = recurringJobManager as MIFCoreRecurringJobManager; if (manager is null) throw new ArgumentException("Parameter 'recurringJobManager' is not of type 'MIFCore.Hangfire.MIFCoreRecurringJobManager'"); - manager.CreateRecurringJob(jobName: jobName, methodCall: methodCall, cronSchedule: cronSchedule, queue: queue, triggerIfNeverExecuted: triggerIfNeverExecuted); + manager.CreateRecurringJob(jobName: jobName, methodCall: methodCall, cronSchedule: cronSchedule, queue: queue, triggerIfNeverExecuted: triggerIfNeverExecuted, timeZone: timeZone); } - public static void CreateRecurringJob(this IRecurringJobManager recurringJobManager, string jobName, Expression> methodCall, string cronSchedule = null, string queue = "default", bool triggerIfNeverExecuted = false) + public static void CreateRecurringJob(this IRecurringJobManager recurringJobManager, string jobName, Expression> methodCall, string cronSchedule = null, string queue = "default", bool triggerIfNeverExecuted = false, TimeZoneInfo timeZone = null) { var manager = recurringJobManager as MIFCoreRecurringJobManager; if (manager is null) throw new ArgumentException("Parameter 'recurringJobManager' is not of type 'MIFCore.Hangfire.MIFCoreRecurringJobManager'"); - manager.CreateRecurringJob(jobName: jobName, methodCall: methodCall, cronSchedule: cronSchedule, queue: queue, triggerIfNeverExecuted: triggerIfNeverExecuted); + manager.CreateRecurringJob(jobName: jobName, methodCall: methodCall, cronSchedule: cronSchedule, queue: queue, triggerIfNeverExecuted: triggerIfNeverExecuted, timeZone: timeZone); } } } diff --git a/MIFCore.Hangfire/MIFCoreRecurringJobManager.cs b/MIFCore.Hangfire/MIFCoreRecurringJobManager.cs index 244b4f1..3bfd6e6 100644 --- a/MIFCore.Hangfire/MIFCoreRecurringJobManager.cs +++ b/MIFCore.Hangfire/MIFCoreRecurringJobManager.cs @@ -22,33 +22,35 @@ public MIFCoreRecurringJobManager(JobStorage jobStorage) this.jobStorage = jobStorage; } - public void CreateRecurringJob(string jobName, Expression> methodCall, string cronSchedule = null, string queue = "default", bool triggerIfNeverExecuted = false) + public void CreateRecurringJob(string jobName, Expression> methodCall, string cronSchedule = null, string queue = "default", bool triggerIfNeverExecuted = false, TimeZoneInfo timeZone = null) { + timeZone = this.GetTimeZoneInfo(jobName, timeZone); cronSchedule = this.GetCronSchedule(jobName, cronSchedule); - var job = Job.FromExpression(methodCall); - this.recurringJobManager.AddOrUpdate( recurringJobId: jobName, methodCall: methodCall, cronExpression: cronSchedule, - timeZone: TimeZoneInfo.Local, - queue: queue); + timeZone: timeZone, + queue: queue + ); if (triggerIfNeverExecuted) this.TriggerRecurringJobIfNeverExecuted(jobName); } - public void CreateRecurringJob(string jobName, Expression> methodCall, string cronSchedule = null, string queue = "default", bool triggerIfNeverExecuted = false) + public void CreateRecurringJob(string jobName, Expression> methodCall, string cronSchedule = null, string queue = "default", bool triggerIfNeverExecuted = false, TimeZoneInfo timeZone = null) { + timeZone = this.GetTimeZoneInfo(jobName, timeZone); cronSchedule = this.GetCronSchedule(jobName, cronSchedule); this.recurringJobManager.AddOrUpdate( - recurringJobId: jobName, - methodCall: methodCall, - cronExpression: cronSchedule, - timeZone: TimeZoneInfo.Local, - queue: queue); + recurringJobId: jobName, + methodCall: methodCall, + cronExpression: cronSchedule, + timeZone: timeZone, + queue: queue + ); if (triggerIfNeverExecuted) this.TriggerRecurringJobIfNeverExecuted(jobName); @@ -70,11 +72,55 @@ public string GetCronSchedule(string jobName, string cronSchedule = null) } private string GetCronFromConfig(string jobName) - { + { var section = Globals.DefaultConfiguration.GetSection(jobName); + + // check if the config value has sub keys for "cron" instead of being set directly on the jobName key. + var cronSubKey = section.GetSection("cron"); + if (cronSubKey.Exists()) + { + return cronSubKey.Value; + } + return section.Exists() ? section.Value : null; } + /// + /// Retreives the TimeZoneInfo for the jobName from the config file or returns the passed value if not found. + /// If no value is passed, the default TimeZoneInfo.Local is returned. + /// + /// + /// + /// + public TimeZoneInfo GetTimeZoneInfo(string jobName, TimeZoneInfo timeZoneInfo = null) + { + // try get config value + var configOverride = this.GetTimeZoneInfoFromConfig(jobName); + + // prefer config > code configured tz > default tz + return configOverride ?? timeZoneInfo ?? TimeZoneInfo.Local; + } + + /// + /// Check for the existence of a timezone config value for the jobName. + /// in .Net 5 the only supported timzone Ids can be found with "tzutil /l" on command line for windows; + /// + /// + /// + private TimeZoneInfo GetTimeZoneInfoFromConfig(string jobName) + { + var section = Globals.DefaultConfiguration.GetSection(jobName); + var tzInfoSubKey = section.GetSection("timezone"); + + // exit early if key doesn't exist or has an empty value. + if (!tzInfoSubKey.Exists() || string.IsNullOrWhiteSpace(tzInfoSubKey.Value)) + { + return null; + } + + return TimeZoneInfo.FindSystemTimeZoneById(tzInfoSubKey.Value); + } + private void TriggerRecurringJobIfNeverExecuted(string jobName) { var connection = this.jobStorage.GetConnection(); diff --git a/MIFCore.TestApp/MIFCore.TestApp.csproj b/MIFCore.TestApp/MIFCore.TestApp.csproj index a0f827d..18bc36c 100644 --- a/MIFCore.TestApp/MIFCore.TestApp.csproj +++ b/MIFCore.TestApp/MIFCore.TestApp.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net5.0 @@ -15,7 +15,7 @@ PreserveNewest - PreserveNewest + Always diff --git a/MIFCore.TestApp/SomeJob.cs b/MIFCore.TestApp/SomeJob.cs index 19108e7..e071b7a 100644 --- a/MIFCore.TestApp/SomeJob.cs +++ b/MIFCore.TestApp/SomeJob.cs @@ -1,4 +1,5 @@ using Hangfire; +using Hangfire.Annotations; using MIFCore.Hangfire; using System; using System.Threading.Tasks; @@ -26,6 +27,14 @@ public Task DoTheJob() return Task.CompletedTask; } + public Task DingleDog([NotNull] string dingledog) { + var current = BackgroundJobContext.Current; + + Console.WriteLine($"dingledog: {dingledog}"); + + return Task.CompletedTask; + } + public Task TriggeredJobAction() { Console.WriteLine("triggered job action"); diff --git a/MIFCore.TestApp/Startup.cs b/MIFCore.TestApp/Startup.cs index 10969b6..d77f7dd 100644 --- a/MIFCore.TestApp/Startup.cs +++ b/MIFCore.TestApp/Startup.cs @@ -1,6 +1,7 @@ using Hangfire; using MIFCore.Hangfire; using Microsoft.Extensions.DependencyInjection; +using System; namespace MIFCore.TestApp { @@ -10,6 +11,8 @@ public void ConfigureServices(IServiceCollection serviceDescriptors) { serviceDescriptors.AddScoped(); serviceDescriptors.AddControllers(); + + } public void Configure() @@ -19,7 +22,13 @@ public void Configure() public void PostConfigure(IBackgroundJobClient backgroundJobClient, IRecurringJobManager recurringJobManager) { - recurringJobManager.CreateRecurringJob("some-job", y => y.DoTheJob(), Cron.Monthly()); + recurringJobManager.CreateRecurringJob("some-job", y => y.DoTheJob(), cronSchedule: "*/5 * * * *"); + recurringJobManager.CreateRecurringJob("some-goober-boy", y => y.DingleDog("goober"), cronSchedule: "*/5 * * * *"); + + // This doesn't seem to run due to the queue? + recurringJobManager.CreateRecurringJob("some-bigboi", y => y.DingleDog("biggest-boi"), cronSchedule: "*/5 * * * *", queue: "bigboi"); + + recurringJobManager.CreateRecurringJob("some-job-triggered", y => y.TriggeredJobAction(), Cron.Monthly()); backgroundJobClient.Enqueue(y => y.DoTheJob()); diff --git a/MIFCore.TestApp/settings.json b/MIFCore.TestApp/settings.json index ef24545..7c86daa 100644 --- a/MIFCore.TestApp/settings.json +++ b/MIFCore.TestApp/settings.json @@ -1,7 +1,15 @@ { "connectionString": "", - "bindingPort": 80, - "bindingPath": "abc", - "instrumentationKey": "" - + "bindingPort": 1337, + "bindingPath": "", + "instrumentationKey": "", + "some-job": "* * * * *", + "some-goober-boy": { + "cron": "* * * * *", + "timezone": "Bougainville Standard Time" + }, + "some-bigboi": { + "cron": "* * * * *", + "timezone": "Samoa Standard Time" + } } \ No newline at end of file