From 8dbba8c64f3959887a7bf4c79edfd98941a5c656 Mon Sep 17 00:00:00 2001 From: mukmyash Date: Fri, 24 Aug 2018 23:33:51 +0500 Subject: [PATCH] Initial commit --- .gitattributes | 63 +++++ .gitignore | 261 ++++++++++++++++++ QuartzHostedService.sln | 58 ++++ src/QuartzHostedService/AssemblyInfo.cs | 3 + src/QuartzHostedService/IJobRegistrator.cs | 9 + .../IJobRegistratorExtensions.cs | 56 ++++ src/QuartzHostedService/IScheduleJob.cs | 11 + .../IServiceCollectionExtensions.cs | 33 +++ src/QuartzHostedService/JobOptions.cs | 11 + src/QuartzHostedService/JobRegistrator.cs | 17 ++ .../QuartzHostedService.cs | 64 +++++ .../QuartzHostedService.csproj | 12 + src/QuartzHostedService/ScheduleJob.cs | 18 ++ .../ServiceCollectionJobFactory.cs | 28 ++ src/QuartzHostedService/TriggerOptions.cs | 26 ++ .../IServiceCollectionExtensionsUnitTest.cs | 75 +++++ .../QuartzHostedService.Test.csproj | 23 ++ .../QuartzHostedServiceUnitTest.cs | 111 ++++++++ 18 files changed, 879 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 QuartzHostedService.sln create mode 100644 src/QuartzHostedService/AssemblyInfo.cs create mode 100644 src/QuartzHostedService/IJobRegistrator.cs create mode 100644 src/QuartzHostedService/IJobRegistratorExtensions.cs create mode 100644 src/QuartzHostedService/IScheduleJob.cs create mode 100644 src/QuartzHostedService/IServiceCollectionExtensions.cs create mode 100644 src/QuartzHostedService/JobOptions.cs create mode 100644 src/QuartzHostedService/JobRegistrator.cs create mode 100644 src/QuartzHostedService/QuartzHostedService.cs create mode 100644 src/QuartzHostedService/QuartzHostedService.csproj create mode 100644 src/QuartzHostedService/ScheduleJob.cs create mode 100644 src/QuartzHostedService/ServiceCollectionJobFactory.cs create mode 100644 src/QuartzHostedService/TriggerOptions.cs create mode 100644 test/QuartzHostedService.Test/IServiceCollectionExtensionsUnitTest.cs create mode 100644 test/QuartzHostedService.Test/QuartzHostedService.Test.csproj create mode 100644 test/QuartzHostedService.Test/QuartzHostedServiceUnitTest.cs diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..a1e1e97 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ef9f00e --- /dev/null +++ b/.gitignore @@ -0,0 +1,261 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +# publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +#*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc \ No newline at end of file diff --git a/QuartzHostedService.sln b/QuartzHostedService.sln new file mode 100644 index 0000000..0689667 --- /dev/null +++ b/QuartzHostedService.sln @@ -0,0 +1,58 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{DD437750-ADAF-4565-9B50-959A92A67100}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuartzHostedService", "src\QuartzHostedService\QuartzHostedService.csproj", "{08939639-5650-4141-9CC7-9B2956701488}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{EDF98575-67B2-4C80-87FC-051AAF2A9E8A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuartzHostedService.Test", "test\QuartzHostedService.Test\QuartzHostedService.Test.csproj", "{B6BE5ED4-B7F3-48C3-8374-8B479F40F5F2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {08939639-5650-4141-9CC7-9B2956701488}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {08939639-5650-4141-9CC7-9B2956701488}.Debug|Any CPU.Build.0 = Debug|Any CPU + {08939639-5650-4141-9CC7-9B2956701488}.Debug|x64.ActiveCfg = Debug|Any CPU + {08939639-5650-4141-9CC7-9B2956701488}.Debug|x64.Build.0 = Debug|Any CPU + {08939639-5650-4141-9CC7-9B2956701488}.Debug|x86.ActiveCfg = Debug|Any CPU + {08939639-5650-4141-9CC7-9B2956701488}.Debug|x86.Build.0 = Debug|Any CPU + {08939639-5650-4141-9CC7-9B2956701488}.Release|Any CPU.ActiveCfg = Release|Any CPU + {08939639-5650-4141-9CC7-9B2956701488}.Release|Any CPU.Build.0 = Release|Any CPU + {08939639-5650-4141-9CC7-9B2956701488}.Release|x64.ActiveCfg = Release|Any CPU + {08939639-5650-4141-9CC7-9B2956701488}.Release|x64.Build.0 = Release|Any CPU + {08939639-5650-4141-9CC7-9B2956701488}.Release|x86.ActiveCfg = Release|Any CPU + {08939639-5650-4141-9CC7-9B2956701488}.Release|x86.Build.0 = Release|Any CPU + {B6BE5ED4-B7F3-48C3-8374-8B479F40F5F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B6BE5ED4-B7F3-48C3-8374-8B479F40F5F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6BE5ED4-B7F3-48C3-8374-8B479F40F5F2}.Debug|x64.ActiveCfg = Debug|Any CPU + {B6BE5ED4-B7F3-48C3-8374-8B479F40F5F2}.Debug|x64.Build.0 = Debug|Any CPU + {B6BE5ED4-B7F3-48C3-8374-8B479F40F5F2}.Debug|x86.ActiveCfg = Debug|Any CPU + {B6BE5ED4-B7F3-48C3-8374-8B479F40F5F2}.Debug|x86.Build.0 = Debug|Any CPU + {B6BE5ED4-B7F3-48C3-8374-8B479F40F5F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B6BE5ED4-B7F3-48C3-8374-8B479F40F5F2}.Release|Any CPU.Build.0 = Release|Any CPU + {B6BE5ED4-B7F3-48C3-8374-8B479F40F5F2}.Release|x64.ActiveCfg = Release|Any CPU + {B6BE5ED4-B7F3-48C3-8374-8B479F40F5F2}.Release|x64.Build.0 = Release|Any CPU + {B6BE5ED4-B7F3-48C3-8374-8B479F40F5F2}.Release|x86.ActiveCfg = Release|Any CPU + {B6BE5ED4-B7F3-48C3-8374-8B479F40F5F2}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {08939639-5650-4141-9CC7-9B2956701488} = {DD437750-ADAF-4565-9B50-959A92A67100} + {B6BE5ED4-B7F3-48C3-8374-8B479F40F5F2} = {EDF98575-67B2-4C80-87FC-051AAF2A9E8A} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C92A5047-9DD9-4F11-9FEE-F1AB1679F94F} + EndGlobalSection +EndGlobal diff --git a/src/QuartzHostedService/AssemblyInfo.cs b/src/QuartzHostedService/AssemblyInfo.cs new file mode 100644 index 0000000..606fbb9 --- /dev/null +++ b/src/QuartzHostedService/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("QuartzHostedService.Test")] \ No newline at end of file diff --git a/src/QuartzHostedService/IJobRegistrator.cs b/src/QuartzHostedService/IJobRegistrator.cs new file mode 100644 index 0000000..367eac9 --- /dev/null +++ b/src/QuartzHostedService/IJobRegistrator.cs @@ -0,0 +1,9 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace QuartzHostedService +{ + public interface IJobRegistrator + { + IServiceCollection Services { get; } + } +} \ No newline at end of file diff --git a/src/QuartzHostedService/IJobRegistratorExtensions.cs b/src/QuartzHostedService/IJobRegistratorExtensions.cs new file mode 100644 index 0000000..c3330ab --- /dev/null +++ b/src/QuartzHostedService/IJobRegistratorExtensions.cs @@ -0,0 +1,56 @@ +using Microsoft.Extensions.DependencyInjection; +using Quartz; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace QuartzHostedService +{ + public static class IJobRegistratorExtensions + { + public static IJobRegistrator RegiserCRONJob( + this IJobRegistrator jobRegistrator, + Action jobOptions) + where TJob : class, IJob + { + var options = new JobOptions(); + jobOptions?.Invoke(options); + + return jobRegistrator.RegiserJob( + options.Triggers?.Select(n => n.CreateTriggerBuilder()) + ); + } + + + public static IJobRegistrator RegiserJob( + this IJobRegistrator jobRegistrator, + Func> triggers) + where TJob : class, IJob + { + + return jobRegistrator.RegiserJob(triggers()); + } + + public static IJobRegistrator RegiserJob( + this IJobRegistrator jobRegistrator, + IEnumerable triggerBuilders) + where TJob : class, IJob + { + jobRegistrator.Services.AddTransient(); + + var jobDetail = JobBuilder.Create().Build(); + + List triggers = new List(triggerBuilders.Count()); + foreach (var triggerBuilder in triggerBuilders) + { + triggers.Add(triggerBuilder.ForJob(jobDetail).Build()); + } + + jobRegistrator.Services.AddSingleton(provider => new ScheduleJob(jobDetail, triggers)); + + return jobRegistrator; + } + } +} diff --git a/src/QuartzHostedService/IScheduleJob.cs b/src/QuartzHostedService/IScheduleJob.cs new file mode 100644 index 0000000..7af8551 --- /dev/null +++ b/src/QuartzHostedService/IScheduleJob.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using Quartz; + +namespace QuartzHostedService +{ + internal interface IScheduleJob + { + IJobDetail JobDetail { get; set; } + IEnumerable Triggers { get; set; } + } +} \ No newline at end of file diff --git a/src/QuartzHostedService/IServiceCollectionExtensions.cs b/src/QuartzHostedService/IServiceCollectionExtensions.cs new file mode 100644 index 0000000..052eab4 --- /dev/null +++ b/src/QuartzHostedService/IServiceCollectionExtensions.cs @@ -0,0 +1,33 @@ +using Microsoft.Extensions.DependencyInjection; +using Quartz; +using Quartz.Impl; +using Quartz.Spi; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Text; + +namespace QuartzHostedService +{ + public static class IServiceCollectionExtensions + { + public static IJobRegistrator UseQuartzHostedService(this IServiceCollection services, + Action stdSchedulerFactoryOptions) + { + services.AddHostedService(); + + services.AddTransient(provider => + { + var options = new NameValueCollection(); + stdSchedulerFactoryOptions?.Invoke(options); + var result = new StdSchedulerFactory(); + if (options.Count > 0) + result.Initialize(options); + return new StdSchedulerFactory(); + }); + services.AddTransient(); + + return new JobRegistrator(services); + } + } +} diff --git a/src/QuartzHostedService/JobOptions.cs b/src/QuartzHostedService/JobOptions.cs new file mode 100644 index 0000000..7cdff51 --- /dev/null +++ b/src/QuartzHostedService/JobOptions.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace QuartzHostedService +{ + public class JobOptions + { + public TriggerOptions[] Triggers { get; set; } + } +} diff --git a/src/QuartzHostedService/JobRegistrator.cs b/src/QuartzHostedService/JobRegistrator.cs new file mode 100644 index 0000000..a3e7ee3 --- /dev/null +++ b/src/QuartzHostedService/JobRegistrator.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Text; + +namespace QuartzHostedService +{ + internal class JobRegistrator : IJobRegistrator + { + public JobRegistrator(IServiceCollection services) + { + Services = services; + } + + public IServiceCollection Services { get; } + } +} diff --git a/src/QuartzHostedService/QuartzHostedService.cs b/src/QuartzHostedService/QuartzHostedService.cs new file mode 100644 index 0000000..aaa7604 --- /dev/null +++ b/src/QuartzHostedService/QuartzHostedService.cs @@ -0,0 +1,64 @@ +using Microsoft.Extensions.Hosting; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Quartz.Impl; +using Quartz; +using Quartz.Spi; +using System.Linq; + +namespace QuartzHostedService +{ + internal class QuartzHostedService : IHostedService + { + private IServiceProvider Services { get; } + private IScheduler _scheduler; + private ISchedulerFactory _schedulerFactory; + private IJobFactory _jobFactory { get; } + + public QuartzHostedService( + IServiceProvider services, + ISchedulerFactory schedulerFactory, + IJobFactory jobFactory) + { + Services = services; + _schedulerFactory = schedulerFactory; + _jobFactory = jobFactory; + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + var _scheduleJobs = Services.GetService>(); + + _scheduler = await _schedulerFactory.GetScheduler(); + _scheduler.JobFactory = _jobFactory; + + await _scheduler.Start(cancellationToken); + + if (_scheduleJobs == null || !_scheduleJobs.Any()) + return; + + foreach (var scheduleJob in _scheduleJobs) + { + bool isNewJob = true; + foreach (var trigger in scheduleJob.Triggers) + { + if (isNewJob) + await _scheduler.ScheduleJob(scheduleJob.JobDetail, trigger, cancellationToken); + else + await _scheduler.ScheduleJob(trigger, cancellationToken); + isNewJob = false; + } + } + } + + public async Task StopAsync(CancellationToken cancellationToken) + { + if (_scheduler.IsStarted) + await _scheduler.Shutdown(cancellationToken); + } + } +} diff --git a/src/QuartzHostedService/QuartzHostedService.csproj b/src/QuartzHostedService/QuartzHostedService.csproj new file mode 100644 index 0000000..4c042e5 --- /dev/null +++ b/src/QuartzHostedService/QuartzHostedService.csproj @@ -0,0 +1,12 @@ + + + + netstandard2.0 + + + + + + + + diff --git a/src/QuartzHostedService/ScheduleJob.cs b/src/QuartzHostedService/ScheduleJob.cs new file mode 100644 index 0000000..8d0d9fc --- /dev/null +++ b/src/QuartzHostedService/ScheduleJob.cs @@ -0,0 +1,18 @@ +using Quartz; +using System; +using System.Collections.Generic; + +namespace QuartzHostedService +{ + internal class ScheduleJob : IScheduleJob + { + public ScheduleJob(IJobDetail jobDetail, IEnumerable triggers) + { + JobDetail = jobDetail; + Triggers = triggers; + } + + public IJobDetail JobDetail { get; set; } + public IEnumerable Triggers { get; set; } + } +} diff --git a/src/QuartzHostedService/ServiceCollectionJobFactory.cs b/src/QuartzHostedService/ServiceCollectionJobFactory.cs new file mode 100644 index 0000000..b5320a3 --- /dev/null +++ b/src/QuartzHostedService/ServiceCollectionJobFactory.cs @@ -0,0 +1,28 @@ +using Quartz; +using Quartz.Spi; +using System; +using System.Collections.Generic; +using System.Text; + +namespace QuartzHostedService +{ + public class ServiceCollectionJobFactory : IJobFactory + { + protected readonly IServiceProvider Container; + + public ServiceCollectionJobFactory(IServiceProvider container) + { + Container = container; + } + + public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) + { + return Container.GetService(bundle.JobDetail.JobType) as IJob; + } + + public void ReturnJob(IJob job) + { + + } + } +} \ No newline at end of file diff --git a/src/QuartzHostedService/TriggerOptions.cs b/src/QuartzHostedService/TriggerOptions.cs new file mode 100644 index 0000000..21b0ca4 --- /dev/null +++ b/src/QuartzHostedService/TriggerOptions.cs @@ -0,0 +1,26 @@ +using Quartz; +using System; +using System.Collections.Generic; +using System.Text; + +namespace QuartzHostedService +{ + public class TriggerOptions + { + public string CRONExpression { get; set; } + + public int? Priority { get; set; } + + public virtual TriggerBuilder CreateTriggerBuilder() + { + var result = TriggerBuilder.Create(); + if (Priority.HasValue) + result.WithPriority(Priority.Value); + + if(!string.IsNullOrEmpty(CRONExpression)) + result.WithCronSchedule(CRONExpression); + + return result; + } + } +} diff --git a/test/QuartzHostedService.Test/IServiceCollectionExtensionsUnitTest.cs b/test/QuartzHostedService.Test/IServiceCollectionExtensionsUnitTest.cs new file mode 100644 index 0000000..edd8e15 --- /dev/null +++ b/test/QuartzHostedService.Test/IServiceCollectionExtensionsUnitTest.cs @@ -0,0 +1,75 @@ +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Quartz; +using Quartz.Impl; +using Quartz.Spi; +using System; +using Xunit; + +namespace QuartzHostedService.Test +{ + public class IServiceCollectionExtensionsUnitTest + { + [Fact(DisplayName = " HostedService")] + public void IServiceCollectionExtensions_Register_HostedService() + { + IServiceCollection serviceCollection = new ServiceCollection(); + IServiceCollectionExtensions.UseQuartzHostedService(serviceCollection, null); + + var testClass = serviceCollection.BuildServiceProvider().GetRequiredService(); + testClass.Should() + .NotBeNull() + .And.BeOfType(); + } + + [Fact(DisplayName = " IJobFactory ( DI Job')")] + public void IServiceCollectionExtensions_Register_IJobFactory() + { + IServiceCollection serviceCollection = new ServiceCollection(); + IServiceCollectionExtensions.UseQuartzHostedService(serviceCollection, null); + + var testClass = serviceCollection.BuildServiceProvider().GetRequiredService(); + testClass.Should() + .NotBeNull() + .And.BeOfType(); + } + + [Fact(DisplayName = " ISchedulerFactory ( )")] + public void IServiceCollectionExtensions_Register_ISchedulerFactory() + { + IServiceCollection serviceCollection = new ServiceCollection(); + IServiceCollectionExtensions.UseQuartzHostedService(serviceCollection, null); + + var testClass = serviceCollection.BuildServiceProvider().GetRequiredService(); + testClass.Should() + .NotBeNull() + .And.BeOfType(); + } + + [Fact(DisplayName = " ISchedulerFactory ( )")] + public void IServiceCollectionExtensions_Register_ISchedulerFactory_WithParams() + { + IServiceCollection serviceCollection = new ServiceCollection(); + IServiceCollectionExtensions.UseQuartzHostedService(serviceCollection, options => { options.Add("quartz.threadPool.threadCount", "1"); }); + + // TODO: + var testClass = serviceCollection.BuildServiceProvider().GetRequiredService(); + testClass.Should() + .NotBeNull() + .And.BeOfType(); + } + + [Fact(DisplayName = " IJobRegistrator")] + public void IServiceCollectionExtensions_Return_IJobRegistrator() + { + IServiceCollection serviceCollection = new ServiceCollection(); + var result = IServiceCollectionExtensions.UseQuartzHostedService(serviceCollection, null); + + result.Should() + .NotBeNull() + .And.BeAssignableTo() + .Subject.Services.Should().Equal(serviceCollection); + } + } +} diff --git a/test/QuartzHostedService.Test/QuartzHostedService.Test.csproj b/test/QuartzHostedService.Test/QuartzHostedService.Test.csproj new file mode 100644 index 0000000..ee834fd --- /dev/null +++ b/test/QuartzHostedService.Test/QuartzHostedService.Test.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp2.0 + + false + + + + + + + + + + + + + + + + + diff --git a/test/QuartzHostedService.Test/QuartzHostedServiceUnitTest.cs b/test/QuartzHostedService.Test/QuartzHostedServiceUnitTest.cs new file mode 100644 index 0000000..e36c008 --- /dev/null +++ b/test/QuartzHostedService.Test/QuartzHostedServiceUnitTest.cs @@ -0,0 +1,111 @@ +using FakeItEasy; +using Quartz; +using Quartz.Spi; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace QuartzHostedService.Test +{ + public class QuartzHostedServiceUnitTest + { + [Fact(DisplayName = "Устанавливаем JobFactory (Для DI)")] + public async void IServiceCollectionExtensions_Register_HostedService() + { + IServiceProvider serviceProvider = A.Fake(); + A.CallTo(() => serviceProvider.GetService(typeof(IEnumerable))).Returns(null); + + ISchedulerFactory schedulerFactory = A.Fake(); + IScheduler scheduler = A.Fake(); + A.CallTo(() => schedulerFactory.GetScheduler(A.Ignored)) + .Returns(Task.FromResult(scheduler)); + + IJobFactory jobFactory = A.Fake(); + + var testClass = new QuartzHostedService(serviceProvider, schedulerFactory, jobFactory); + await testClass.StartAsync(CancellationToken.None); + + A.CallTo(scheduler) + .Where(a => a.Method.Name.Equals("set_JobFactory")); + } + + + [Fact(DisplayName = "Запустили все зарегистрированые Job")] + public async void IServiceCollectionExtensions_Register_RegisterJob() + { + // Зарегистрированные джобы + var scheduleJobc = new List(); + var jobDetail1 = A.Fake(); + var jobDetail2 = A.Fake(); + var jobDetail3 = A.Fake(); + + scheduleJobc.Add(new ScheduleJob(jobDetail1, new List() { A.Fake() })); + scheduleJobc.Add(new ScheduleJob(jobDetail2, new List() { A.Fake() })); + scheduleJobc.Add(new ScheduleJob(jobDetail3, new List() { A.Fake() })); + + IServiceProvider serviceProvider = A.Fake(); + A.CallTo(() => serviceProvider.GetService(typeof(IEnumerable))).Returns(scheduleJobc); + + + ISchedulerFactory schedulerFactory = A.Fake(); + IScheduler scheduler = A.Fake(); + A.CallTo(() => schedulerFactory.GetScheduler(A.Ignored)) + .Returns(Task.FromResult(scheduler)); + + IJobFactory jobFactory = A.Fake(); + + var testClass = new QuartzHostedService(serviceProvider, schedulerFactory, jobFactory); + await testClass.StartAsync(CancellationToken.None); + + A.CallTo( + () => scheduler.ScheduleJob( + A.That.Matches(jd => jd == jobDetail1 || jd == jobDetail2 || jd == jobDetail3), + A.Ignored, + A.Ignored)) + .MustHaveHappened(Repeated.Like(count => count == 3)); + } + + [Fact(DisplayName = "Запустили все зарегистрированые Job с указанными ITrigger")] + public async void IServiceCollectionExtensions_Register_RegisterJob_ITrigger() + { + // Зарегистрированные джобы + var scheduleJobc = new List(); + var jobDetail1 = A.Fake(); + + var trigger1 = A.Fake(); + var trigger2 = A.Fake(); + var trigger3 = A.Fake(); + scheduleJobc.Add(new ScheduleJob(jobDetail1, new List() { trigger1, trigger2, trigger3 })); + + IServiceProvider serviceProvider = A.Fake(); + A.CallTo(() => serviceProvider.GetService(typeof(IEnumerable))).Returns(scheduleJobc); + + + ISchedulerFactory schedulerFactory = A.Fake(); + IScheduler scheduler = A.Fake(); + A.CallTo(() => schedulerFactory.GetScheduler(A.Ignored)) + .Returns(Task.FromResult(scheduler)); + + IJobFactory jobFactory = A.Fake(); + + var testClass = new QuartzHostedService(serviceProvider, schedulerFactory, jobFactory); + await testClass.StartAsync(CancellationToken.None); + + A.CallTo( + () => scheduler.ScheduleJob( + A.That.Matches(jd => jd == jobDetail1), + A.That.Matches(t => t == trigger1), + A.Ignored)) + .MustHaveHappened(Repeated.Exactly.Once); + + A.CallTo( + () => scheduler.ScheduleJob( + A.That.Matches(t => t == trigger2 || t == trigger3), + A.Ignored)) + .MustHaveHappened(Repeated.Exactly.Twice); + } + } +}