Skip to content

Commit

Permalink
feat: Added dependency injection extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
pksorensen committed May 19, 2022
1 parent af4bfbc commit 98eb6f4
Show file tree
Hide file tree
Showing 8 changed files with 254 additions and 6 deletions.
5 changes: 5 additions & 0 deletions src/WorkflowEngine.Core/IActionImplementationExtenssions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ public static IServiceCollection AddAction<T>(this IServiceCollection services,
return services.AddTransient<T>()
.AddSingleton< IActionImplementationMetadata>(new ActionImplementationMetadata<T> { Type = type ?? typeof(T).Name });
}

public static IServiceCollection AddWorkflow<T>(this IServiceCollection services) where T :class, IWorkflow
{
return services.AddTransient<IWorkflow, T>();
}
}


Expand Down
20 changes: 14 additions & 6 deletions src/WorkflowEngine.Core/WorkflowEngine.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,20 @@

<ItemGroup>
<PackageReference Include="Delegate.ExpressionEngine" Version="2.0.0-dev.14" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
</ItemGroup>

<ItemGroup>
<Folder Include="Actions\" />

</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)'=='netcoreapp3.1'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net5.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net6.0'">
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
</ItemGroup>

</Project>
37 changes: 37 additions & 0 deletions src/WorkflowEngine.Hangfire/DependencyInjectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Microsoft.Extensions.DependencyInjection;
using WorkflowEngine;
using WorkflowEngine.Core;
using WorkflowEngine.Core.Actions;
using WorkflowEngine.Core.Expressions;

namespace Microsoft.Extensions.DependencyInjection
{

public static class DependencyInjectionExtensions
{
public static IServiceCollection AddWorkflowEngine<TOutputsRepository>(this IServiceCollection services)
where TOutputsRepository: class, IOutputsRepository
{
services.AddTransient<IWorkflowExecutor, WorkflowExecutor>();
services.AddTransient<IActionExecutor, ActionExecutor>();
services.AddTransient<IHangfireWorkflowExecutor, HangfireWorkflowExecutor>();
services.AddTransient<IHangfireActionExecutor, HangfireWorkflowExecutor>();
services.AddTransient(typeof(ScheduledWorkflowTrigger<>));

services.AddScoped<IArrayContext, ArrayContext>();
services.AddScoped<IScopeContext, ScopeContext>();
services.AddScoped<IRunContextAccessor, RunContextFactory>();
services.AddAction<ForeachAction>("Foreach");

services.AddFunctions();
services.AddScoped<IOutputsRepository, TOutputsRepository>();
services.AddSingleton<IWorkflowRepository, DefaultWorkflowRepository>();

services.AddHostedService<WorkflowStarterBackgroundJob>();

return services;
}


}
}
26 changes: 26 additions & 0 deletions src/WorkflowEngine.Hangfire/HangfireExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Hangfire.Storage;
using System.Collections.Generic;

namespace WorkflowEngine
{
public static class HangfireExtensions
{


public static void SetJobExternalKey(this IStorageConnection connection, string externalId, string jobId)
{
// This method can be implemented in 1.1.0
connection.SetRangeInHash($"x-backgroundjob-keys:{externalId}", new[] { new KeyValuePair<string, string>("JobId", jobId) });
}

public static string GetJobIdByKey(this IStorageConnection connection, string externalId)
{
// This method can be implemented in 1.1.0
var entries = connection.GetAllEntriesFromHash($"x-backgroundjob-keys:{externalId}");
if (entries == null || !entries.ContainsKey("JobId"))
return null;

return entries["JobId"];
}
}
}
31 changes: 31 additions & 0 deletions src/WorkflowEngine.Hangfire/JobNaturalKeyAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Hangfire.Client;
using Hangfire.Common;
using System;
using System.Linq;

namespace WorkflowEngine
{

public class JobNaturalKeyAttribute : JobFilterAttribute, IClientFilter
{
public JobNaturalKeyAttribute(string keyFormat)
{
KeyFormat = keyFormat;
}

public string KeyFormat { get; private set; }

public void OnCreated(CreatedContext filterContext)
{
var key = String.Format(KeyFormat, args: filterContext.Job.Args.ToArray());
filterContext.Connection.SetJobExternalKey(key, filterContext.BackgroundJob.Id);

}

public void OnCreating(CreatingContext filterContext)
{


}
}
}
57 changes: 57 additions & 0 deletions src/WorkflowEngine.Hangfire/ScheduledWorkflowTrigger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using Hangfire;
using Hangfire.Client;
using Hangfire.Common;
using Hangfire.Storage;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WorkflowEngine.Core;

namespace WorkflowEngine
{

public class ScheduledWorkflowTrigger<TWorkflow> where TWorkflow : IWorkflow, new()
{
private readonly ILogger _logger;
private readonly IBackgroundJobClient _backgroundJobClient;
private readonly IStorageConnection _storageConnection;

public ScheduledWorkflowTrigger(ILogger<ScheduledWorkflowTrigger<TWorkflow>> logger, IBackgroundJobClient backgroundJobClient, JobStorage storageConnection)
{
_logger = logger;
_backgroundJobClient = backgroundJobClient;
_storageConnection = storageConnection.GetConnection();
}

[JobNaturalKey("{0}")]
public Task<string> Trigger(string externalid, bool create, DateTimeOffset time, Dictionary<string, object> inputs)
{
var existingJob = _storageConnection.GetJobIdByKey(externalid);
if (!string.IsNullOrEmpty(existingJob))
{
_logger.LogInformation("Cleaning up existing Hangfire Job {JobID}", existingJob);
_backgroundJobClient.Delete(existingJob);
}

if (create)
{
var workflow = new TWorkflow();
workflow.Manifest.Triggers.First().Value.Inputs = inputs;

var job = _backgroundJobClient.Schedule<IHangfireWorkflowExecutor>((executor) => executor.TriggerAsync(
new TriggerContext { Workflow = workflow, }), time);

_logger.LogInformation("Created scheduled workflow job {JobID}", job);

return Task.FromResult(job);
}

return Task.FromResult<string>(null);
}

}
}
14 changes: 14 additions & 0 deletions src/WorkflowEngine.Hangfire/WorkflowEngine.Hangfire.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,26 @@
<Authors>Delegate A/S</Authors>
<PackageDescription>WorkflowEngine used to execute workflows based on Json description.</PackageDescription>
<RepositoryUrl>https://github.com/delegateas/WorkflowEngine</RepositoryUrl>
<RootNamespace>WorkflowEngine</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Hangfire.Core" Version="1.7.28" />

</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)'=='netcoreapp3.1'">
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net5.0'">
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net6.0'">
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
</ItemGroup>



<ItemGroup>
<ProjectReference Include="..\WorkflowEngine.Core\WorkflowEngine.Core.csproj" />
</ItemGroup>
Expand Down
70 changes: 70 additions & 0 deletions src/WorkflowEngine.Hangfire/WorkflowStarterBackgroundJob.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using Hangfire;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using WorkflowEngine;
using WorkflowEngine.Core;

namespace Microsoft.Extensions.DependencyInjection
{
public class WorkflowStarterBackgroundJob : BackgroundService
{
private readonly IServiceScopeFactory _serviceScopeFactory;

public WorkflowStarterBackgroundJob(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var first = true;
while (!stoppingToken.IsCancellationRequested)
{
using var scope = _serviceScopeFactory.CreateScope();
var sp = scope.ServiceProvider;

var workflows = sp.GetRequiredService<IWorkflowRepository>();
var jobs = sp.GetRequiredService<IRecurringJobManager>();

foreach (var workflow in await workflows.GetAllWorkflows())
{
var trigger = workflow.Manifest.Triggers.FirstOrDefault(t => t.Value.Type == "TimerTrigger");

if (!trigger.Equals(default(KeyValuePair<string, TriggerMetadata>)))
{

jobs.AddOrUpdate<IHangfireWorkflowExecutor>(workflow.Id.ToString(),
(executor) => executor.TriggerAsync(new TriggerContext
{
Workflow = workflow,
Trigger = new Trigger
{
Inputs = trigger.Value.Inputs,
ScheduledTime = DateTimeOffset.UtcNow,
Type = workflow.Manifest.Triggers.FirstOrDefault().Value.Type,
Key = trigger.Key
},
}), trigger.Value.Inputs["cronExpression"] as string);

if (first && trigger.Value.Inputs.ContainsKey("runAtStartup") && (bool)trigger.Value.Inputs["runAtStartup"])
jobs.Trigger(workflow.Id.ToString());
}
}



first = false;
await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);



}


}
}
}

0 comments on commit 98eb6f4

Please sign in to comment.