Skip to content

Commit

Permalink
Remove outdated cron job implementation
Browse files Browse the repository at this point in the history
This change removes the existing cron job implementation, which included removing several classes related to Cron job processing and scheduling. This significant update was necessary to pave the way for a new, improved implementation that promises better efficiency and maintainability. Please note that any classes utilizing this previous cron job scheme will need to be updated.
  • Loading branch information
frankhaugen committed Feb 3, 2024
1 parent 982b129 commit 280acb1
Show file tree
Hide file tree
Showing 30 changed files with 766 additions and 554 deletions.
24 changes: 24 additions & 0 deletions Frank.CronJobs.Cron/CronHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,30 @@
/// </summary>
public static class CronHelper
{
/// <summary>
/// Parses a cron expression and returns a CronExpression object.
/// </summary>
/// <param name="expression">The cron expression to parse.</param>
/// <returns>A CronExpression object representing the parsed cron expression.</returns>
public static CronExpression Parse(string expression) => new(expression);

/// <summary>
/// Tries to parse the provided cron expression and creates a CronExpression object if the expression is valid.
/// </summary>
/// <param name="expression">The cron expression to parse.</param>
/// <param name="cronExpression">When this method returns, contains the CronExpression object created from the parsed expression if the expression is valid; otherwise, contains null.</param>
/// <returns>Returns true if the cron expression was successfully parsed and created into a CronExpression object; otherwise, returns false.</returns>
public static bool TryParse(string expression, out CronExpression? cronExpression)
{
if (IsValid(expression))
{
cronExpression = new CronExpression(expression);
return true;
}
cronExpression = null;
return false;
}

/// <summary>
/// Calculates the next occurrence of a cron expression based on the current UTC time.
/// </summary>
Expand Down
90 changes: 90 additions & 0 deletions Frank.CronJobs.Cron/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Frank.CronJobs.Cron

Frank.CronJobs.Cron is a .NET library that provides cron expression parsing and scheduling capabilities. Its meant as an
internal dependency for Frank.CronJobs, but works just fine as a CronParser

## Features

- Parse cron expressions and validate syntax
- Calculate next occurrence datetime based on cron expression
- Determine if cron expression is due relative to specified datetime
- Helper methods for working with cron expressions
- Constants for common Cron Expressions

## Usage
Parse and validate cron expression

```csharp
string expression = "0 15 10 * * ?";

CronExpression cron = new CronExpression(expression);

bool isValid = cron.IsValid;
```

Calculate next occurrence

```csharp
string expression = "0 15 10 * * ?";

DateTime next = CronExpression.GetNextOccurrence(expression);
```

Check if cron is due

```csharp
string expression = "0 15 10 * * *";
DateTime dateTime = new DateTime(2023, 2, 15, 11, 0, 0);

bool isDue = CronExpression.IsDue(expression, dateTime); // true
```

Use helper methods

```csharp
// Get next occurrence from current time
DateTime next = CronHelper.GetNextOccurrence(expression);

// Get time until next occurrence
TimeSpan timeToNext = CronHelper.GetTimeUntilNextOccurrence(expression);

// Check if due
bool isDue = CronHelper.IsDue(expression);
```

Use common cron expressions

```csharp
string everySecond = PredefinedCronExpressions.EverySecond;
string everyMinute = PredefinedCronExpressions.EveryMinute;
string everyHour = PredefinedCronExpressions.EveryHour;
string everyDay = PredefinedCronExpressions.EveryDay;
string everyWeek = PredefinedCronExpressions.EveryWeek;
string everyMonth = PredefinedCronExpressions.EveryMonth;
string everyYear = PredefinedCronExpressions.EveryYear;

string everyYearOnChristmasEve = PredefinedCronExpressions.EveryYearOn.ChristmasEve;
```

## Installation
Install the NuGet package directly from the package manager console:

```powershell
PM> Install-Package Frank.CronJobs.Cron
```

## License

Frank.CronJobs.Cron is licensed under the [MIT license](../LICENSE).

## Contributing

Contributions, except for actual bug fixes, are not welcome at this time. This is an internal dependency for Frank.CronJobs,
and though it is a standalone library, it is not meant to be developed as such. If you have a bug fix, please submit a pull
with a test that demonstrates the bug and the fix.

## Credits

This library is based on [CronQuery](https://github.com/marxjmoura/cronquery), which I am a contributor to. This is built on
that code to change it in a few ways to better suit my needs for Frank.CronJobs, and make it a standalone library so the
lightweight cron parsing can be used in other projects as well with no dependencies.
16 changes: 16 additions & 0 deletions Frank.CronJobs.SampleApp/Frank.CronJobs.SampleApp.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Frank.CronJobs\Frank.CronJobs.csproj" />
</ItemGroup>

</Project>
40 changes: 40 additions & 0 deletions Frank.CronJobs.SampleApp/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// See https://aka.ms/new-console-template for more information

using System.Text.Json;
using Frank.CronJobs;
using Frank.CronJobs.Cron;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

Console.WriteLine("Hello, World!");

var builder = new HostBuilder();
builder.ConfigureLogging(logging =>
{
logging.AddJsonConsole(options =>
{
options.JsonWriterOptions = new JsonWriterOptions
{
Indented = true,
};
options.IncludeScopes = true;
options.TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff";
options.UseUtcTimestamp = true;
});
});
builder.ConfigureServices((context, services) =>
{
services.AddCronJob<MyService>(PredefinedCronExpressions.EverySecond);
});

await builder.RunConsoleAsync();

public class MyService(ILogger<MyService> logger) : ICronJob
{
/// <inheritdoc />
public async Task RunAsync(CancellationToken cancellationToken)
{
logger.LogInformation("Running");
await Task.Delay(100, cancellationToken);
}
}
32 changes: 32 additions & 0 deletions Frank.CronJobs.Tests/CronJobSchedulerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Frank.CronJobs.Cron;
using Frank.Testing.TestBases;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Xunit.Abstractions;

namespace Frank.CronJobs.Tests;

public class CronJobSchedulerTests(ITestOutputHelper outputHelper) : HostApplicationTestBase(outputHelper)
{
/// <inheritdoc />
protected override Task SetupAsync(HostApplicationBuilder builder)
{
builder.Services.AddCronJob<MyService>(PredefinedCronExpressions.EverySecond);
return Task.CompletedTask;
}

[Fact]
public async Task Test()
{
await Task.Delay(5000);
}

private class MyService(ILogger<MyService> logger) : ICronJob
{
public async Task RunAsync(CancellationToken cancellationToken)
{
logger.LogInformation("Running");
await Task.Delay(100, cancellationToken);
}
}
}

This file was deleted.

1 change: 1 addition & 0 deletions Frank.CronJobs.Tests/Frank.CronJobs.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Frank.Testing.Logging" Version="1.6.0" />
<PackageReference Include="Frank.Testing.TestBases" Version="1.6.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using FluentAssertions;
using Frank.CronJobs.Cron;
using Frank.Testing.TestBases;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Xunit.Abstractions;

namespace Frank.CronJobs.Tests.ScheduleMaintainer;

public class ScheduleMaintainerChangeScheduleTests(ITestOutputHelper outputHelper) : HostApplicationTestBase(outputHelper)
{
private readonly List<DateTime> _runTimes = new();

/// <inheritdoc />
protected override Task SetupAsync(HostApplicationBuilder builder)
{
builder.Services.AddCronJob<MyService>(PredefinedCronExpressions.EverySecond);
builder.Services.AddSingleton(_runTimes);
return Task.CompletedTask;
}

[Fact]
public async Task Test()
{
await Task.Delay(1500);
_runTimes.Should().HaveCount(1);
await Task.Delay(6000);
_runTimes.Should().HaveCountGreaterThan(1);
}

private class MyService(ILogger<MyService> logger, List<DateTime> runTimes, IScheduleMaintainer maintainer) : ICronJob
{
public async Task RunAsync(CancellationToken cancellationToken)
{
logger.LogInformation("Running");
runTimes.Add(DateTime.UtcNow);
await Task.Delay(250, cancellationToken);
maintainer.SetSchedule<MyService>(PredefinedCronExpressions.EveryFiveSeconds);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using FluentAssertions;
using Frank.CronJobs.Cron;
using Frank.Testing.TestBases;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Xunit.Abstractions;

namespace Frank.CronJobs.Tests.ScheduleMaintainer;

public class ScheduleMaintainerRestartJobTest(ITestOutputHelper outputHelper) : HostApplicationTestBase(outputHelper)
{
private readonly List<Version> _runVersions = new();

/// <inheritdoc />
protected override Task SetupAsync(HostApplicationBuilder builder)
{
builder.Services.AddCronJob<MyRestartingService>(PredefinedCronExpressions.EverySecond);
builder.Services.AddSingleton(_runVersions);
return Task.CompletedTask;
}

[Fact]
public async Task Test3()
{
await Task.Delay(1500);
_runVersions.Should().HaveCount(1);
await Task.Delay(6000);
_runVersions.Should().HaveCount(1);
var maintainer = Services.GetRequiredService<IScheduleMaintainer>();
maintainer.Start<MyRestartingService>();
await Task.Delay(1000);
_runVersions.Should().HaveCountGreaterOrEqualTo(2);
}

private class MyRestartingService(ILogger<MyRestartingService> logger, List<Version> runVersions, IScheduleMaintainer maintainer) : ICronJob
{
public async Task RunAsync(CancellationToken cancellationToken)
{
logger.LogInformation("Running restart service");
runVersions.Add(new Version(1, 0, 0, 0));
await Task.Delay(250, cancellationToken);
maintainer.Stop<MyRestartingService>();
}
}
}
Loading

0 comments on commit 280acb1

Please sign in to comment.