Skip to content

Commit

Permalink
feat: Made runtime registry more restrictive
Browse files Browse the repository at this point in the history
  • Loading branch information
linkdotnet committed Nov 3, 2024
1 parent 3adfd18 commit 8526904
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 14 deletions.
11 changes: 10 additions & 1 deletion docs/migration/v4.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,13 @@ The `AddNCronJob(Delegate)` (Minimal API) was a different static class than the
```diff
- IServiceCollection.AddNCronJob(() => {}, "* * * * *");
+ NCronJobExtensions.AddNCronJob(services, () => {}, "* * * * *");
```
```

## `IRuntimeRegistry` is more restrictive
In `v3` the `IRuntimeRegistry` offered the ability to use the whole `NCronJobOptionBuilder` which lead to confusion, especially if done something like this:

```csharp
runtimeRegistry.AddJob(n => n.AddJob<MyJob>(...).RunAtStartup().AddExceptionHandler<MyHandler>());
```

It didn't make sense to add a startup job during runtime. Also adding exception handlers during runtime was out of scope for this feature. Therefore the interface is more restrictive now and only allows to add jobs.
37 changes: 37 additions & 0 deletions src/NCronJob/Configuration/Builder/IRuntimeJobBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
namespace NCronJob;

/// <summary>
/// Represents a builder for adding jobs at runtime.
/// </summary>
public interface IRuntimeJobBuilder
{
/// <summary>
/// Adds a job to the service collection that gets executed based on the given cron expression.
/// </summary>
/// <param name="options">Configures the <see cref="JobOptionBuilder"/>, like the cron expression or parameters that get passed down.</param>
/// <returns>Returns the <see cref="IRuntimeJobBuilder"/> for chaining.</returns>
IRuntimeJobBuilder AddJob<TJob>(Action<JobOptionBuilder>? options = null) where TJob : class, IJob;

/// <summary>
/// Adds a job to the service collection that gets executed based on the given cron expression.
/// </summary>
/// <param name="jobType">The type of the job to be added.</param>
/// <param name="options">Configures the <see cref="JobOptionBuilder"/>, like the cron expression or parameters that get passed down.</param>
/// <returns>Returns the <see cref="IRuntimeJobBuilder"/> for chaining.</returns>
IRuntimeJobBuilder AddJob(Type jobType, Action<JobOptionBuilder>? options = null);

/// <summary>
/// Adds a job using an asynchronous anonymous delegate to the service collection that gets executed based on the given cron expression.
/// </summary>
/// <param name="jobDelegate">The delegate that represents the job to be executed.</param>
/// <param name="cronExpression">The cron expression that defines when the job should be executed.</param>
/// <param name="timeZoneInfo">The time zone information that the cron expression should be evaluated against.
/// If not set the default time zone is UTC.
/// </param>
/// <param name="jobName">Sets the job name that can be used to identify and manipulate the job later on.</param>
/// <returns>Returns the <see cref="IRuntimeJobBuilder"/> for chaining.</returns>
IRuntimeJobBuilder AddJob(Delegate jobDelegate,
string cronExpression,
TimeZoneInfo? timeZoneInfo = null,
string? jobName = null);
}
37 changes: 26 additions & 11 deletions src/NCronJob/Configuration/Builder/NCronJobOptionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ namespace NCronJob;
/// <summary>
/// Represents the builder for the NCronJob options.
/// </summary>
public class NCronJobOptionBuilder : IJobStage
public class NCronJobOptionBuilder : IJobStage, IRuntimeJobBuilder
{
private protected readonly IServiceCollection Services;
private protected readonly ConcurrencySettings Settings;
private readonly IServiceCollection services;
private readonly ConcurrencySettings settings;
private readonly JobRegistry jobRegistry;

internal NCronJobOptionBuilder(
IServiceCollection services,
ConcurrencySettings settings,
JobRegistry jobRegistry)
{
Services = services;
Settings = settings;
this.services = services;
this.settings = settings;
this.jobRegistry = jobRegistry;
}

Expand All @@ -41,7 +41,7 @@ public IStartupStage<T> AddJob<T>(Action<JobOptionBuilder>? options = null)
where T : class, IJob
{
var builder = AddJobInternal(typeof(T), options);
return new StartupStage<T>(Services, Settings, jobRegistry, builder);
return new StartupStage<T>(services, settings, jobRegistry, builder);
}

/// <summary>
Expand All @@ -60,7 +60,7 @@ public IStartupStage<T> AddJob<T>(Action<JobOptionBuilder>? options = null)
public IStartupStage<IJob> AddJob(Type jobType, Action<JobOptionBuilder>? options = null)
{
var builder = AddJobInternal(jobType, options);
return new StartupStage<IJob>(Services, Settings, jobRegistry, builder);
return new StartupStage<IJob>(services, settings, jobRegistry, builder);
}

/// <summary>
Expand Down Expand Up @@ -103,10 +103,25 @@ public NCronJobOptionBuilder AddJob(
/// </remarks>
public NCronJobOptionBuilder AddExceptionHandler<TExceptionHandler>() where TExceptionHandler : class, IExceptionHandler
{
Services.AddSingleton<IExceptionHandler, TExceptionHandler>();
services.AddSingleton<IExceptionHandler, TExceptionHandler>();
return this;
}

IRuntimeJobBuilder IRuntimeJobBuilder.AddJob<TJob>(Action<JobOptionBuilder>? options)
{
AddJob<TJob>(options);
return this;
}

IRuntimeJobBuilder IRuntimeJobBuilder.AddJob(Type jobType, Action<JobOptionBuilder>? options)
{
AddJob(jobType, options);
return this;
}

IRuntimeJobBuilder IRuntimeJobBuilder.AddJob(Delegate jobDelegate, string cronExpression, TimeZoneInfo? timeZoneInfo, string? jobName) =>
AddJob(jobDelegate, cronExpression, timeZoneInfo, jobName);

private void ValidateConcurrencySetting(object jobIdentifier)
{
var cachedJobAttributes = jobIdentifier switch
Expand All @@ -117,11 +132,11 @@ private void ValidateConcurrencySetting(object jobIdentifier)
};

var concurrencyAttribute = cachedJobAttributes.ConcurrencyPolicy;
if (concurrencyAttribute != null && concurrencyAttribute.MaxDegreeOfParallelism > Settings.MaxDegreeOfParallelism)
if (concurrencyAttribute != null && concurrencyAttribute.MaxDegreeOfParallelism > settings.MaxDegreeOfParallelism)
{
var name = jobIdentifier is Type type ? type.Name : ((MethodInfo)jobIdentifier).Name;
throw new InvalidOperationException(
$"The MaxDegreeOfParallelism for {name} ({concurrencyAttribute.MaxDegreeOfParallelism}) cannot exceed the global limit ({Settings.MaxDegreeOfParallelism}).");
$"The MaxDegreeOfParallelism for {name} ({concurrencyAttribute.MaxDegreeOfParallelism}) cannot exceed the global limit ({settings.MaxDegreeOfParallelism}).");
}
}

Expand All @@ -143,7 +158,7 @@ private JobOptionBuilder AddJobInternal(Type jobType, Action<JobOptionBuilder>?
var builder = new JobOptionBuilder();
options?.Invoke(builder);

Services.TryAddScoped(jobType);
services.TryAddScoped(jobType);

var jobOptions = builder.GetJobOptions();

Expand Down
4 changes: 2 additions & 2 deletions src/NCronJob/Registry/RuntimeJobRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public interface IRuntimeJobRegistry
/// <summary>
/// Gives the ability to add a job.
/// </summary>
void AddJob(Action<NCronJobOptionBuilder> jobBuilder);
void AddJob(Action<IRuntimeJobBuilder> jobBuilder);

/// <summary>
/// Removes the job with the given name.
Expand Down Expand Up @@ -121,7 +121,7 @@ public RuntimeJobRegistry(
}

/// <inheritdoc />
public void AddJob(Action<NCronJobOptionBuilder> jobBuilder)
public void AddJob(Action<IRuntimeJobBuilder> jobBuilder)
{
var builder = new NCronJobOptionBuilder(services, concurrencySettings, jobRegistry);
jobBuilder(builder);
Expand Down

0 comments on commit 8526904

Please sign in to comment.