Incremental work on scheduling activities

This commit is contained in:
Sipke Schoorstra 2022-01-13 22:38:01 +01:00
parent 803f74b8b5
commit 1949221b23
29 changed files with 458 additions and 55 deletions

View file

@ -80,6 +80,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Serialization", "src\c
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Scripting.Liquid", "src\scripting\Elsa.Scripting.Liquid\Elsa.Scripting.Liquid.csproj", "{A7473430-F228-4534-AEA5-AA8D80E09364}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "modules", "modules", "{5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Modules.Quartz", "src\modules\Elsa.Modules.Quartz\Elsa.Modules.Quartz.csproj", "{7D5A49B4-9A9B-496E-803B-DEB85B2C3132}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -170,6 +174,10 @@ Global
{A7473430-F228-4534-AEA5-AA8D80E09364}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A7473430-F228-4534-AEA5-AA8D80E09364}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A7473430-F228-4534-AEA5-AA8D80E09364}.Release|Any CPU.Build.0 = Release|Any CPU
{7D5A49B4-9A9B-496E-803B-DEB85B2C3132}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7D5A49B4-9A9B-496E-803B-DEB85B2C3132}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7D5A49B4-9A9B-496E-803B-DEB85B2C3132}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7D5A49B4-9A9B-496E-803B-DEB85B2C3132}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{155227F0-A33B-40AA-A4B4-06F813EB921B} = {61017E64-6D00-49CB-9E81-5002DC8F7D5F}
@ -205,5 +213,7 @@ Global
{820016B7-01CD-4032-8239-6A4E53F1A352} = {9F8AE7FB-E5F9-4DCB-9CF8-0362B3D18DAA}
{55F9B33D-5FAF-4355-97EF-445C93208E85} = {C6658DE0-2B2F-47F0-BB61-2CA66D435C09}
{A7473430-F228-4534-AEA5-AA8D80E09364} = {2633B8B9-4AEC-4A54-8832-1C940B54E041}
{5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79} = {61017E64-6D00-49CB-9E81-5002DC8F7D5F}
{7D5A49B4-9A9B-496E-803B-DEB85B2C3132} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79}
EndGlobalSection
EndGlobal

View file

@ -2,13 +2,12 @@
using System.Linq;
using System.Net.Http;
using Elsa.Attributes;
using Elsa.Contracts;
using Elsa.Management.Models;
using Elsa.Models;
namespace Elsa.Activities.Http;
public class HttpTrigger : Trigger
public class HttpTrigger : TriggerActivity
{
[Input] public Input<string> Path { get; set; } = default!;
@ -20,32 +19,14 @@ public class HttpTrigger : Trigger
[Output] public Output<HttpRequestModel>? Result { get; set; }
protected override IEnumerable<object> GetHashInputs(TriggerIndexingContext context)
protected override IEnumerable<object> GetHashInputs(TriggerIndexingContext context) => GetHashInputs(context.ExpressionExecutionContext);
protected override void Execute(ActivityExecutionContext context) => context.SetBookmarks(GetHashInputs(context.ExpressionExecutionContext));
private IEnumerable<object> GetHashInputs(ExpressionExecutionContext context)
{
var path = context.ExpressionExecutionContext.Get(Path);
var methods = context.ExpressionExecutionContext.Get(SupportedMethods);
// Generate a bookmark hash for path and selected methods.
var path = context.Get(Path);
var methods = context.Get(SupportedMethods);
return methods!.Select(x => (path!.ToLowerInvariant(), x.ToLowerInvariant())).Cast<object>().ToArray();
}
protected override void Execute(ActivityExecutionContext context)
{
var bookmarks = CreateBookmarks(context).ToList();
context.SetBookmarks(bookmarks);
}
private IEnumerable<Bookmark> CreateBookmarks(ActivityExecutionContext context)
{
var path = context.Get(Path)!;
var methods = context.Get(SupportedMethods)!;
var hasher = context.GetRequiredService<IHasher>();
var identityGenerator = context.GetRequiredService<IIdentityGenerator>();
foreach (var method in methods)
{
var hashInput = (path.ToLowerInvariant(), method.ToLowerInvariant());
var hash = hasher.Hash(hashInput);
var bookmarkId = identityGenerator.GenerateId();
yield return new Bookmark(bookmarkId, NodeType, hash, Id, context.Id);
}
}
}

View file

@ -0,0 +1,10 @@
using System.Threading;
using System.Threading.Tasks;
namespace Elsa.Activities.Scheduling.Contracts;
public interface IJob
{
string JobId { get; }
Task ExecuteAsync(CancellationToken cancellationToken);
}

View file

@ -0,0 +1,9 @@
using System.Threading;
using System.Threading.Tasks;
namespace Elsa.Activities.Scheduling.Contracts;
public interface IJobScheduler
{
Task ScheduleAsync(IJob job, ISchedule schedule, CancellationToken cancellationToken = default);
}

View file

@ -0,0 +1,5 @@
namespace Elsa.Activities.Scheduling.Contracts;
public interface ISchedule
{
}

View file

@ -10,4 +10,8 @@
<ProjectReference Include="..\..\core\Elsa.Core\Elsa.Core.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Services" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,27 @@
using System.Threading;
using System.Threading.Tasks;
using Elsa.Activities.Scheduling.Contracts;
namespace Elsa.Activities.Scheduling.Jobs;
public class ResumeWorkflowJob : IJob
{
public ResumeWorkflowJob()
{
}
public ResumeWorkflowJob(string workflowInstanceId, string activityId)
{
WorkflowInstanceId = workflowInstanceId;
ActivityId = activityId;
}
public string JobId => $"workflow-instance:{WorkflowInstanceId}-{ActivityId}";
public string WorkflowInstanceId { get; init; } = default!;
public string ActivityId { get; init; } = default!;
public Task ExecuteAsync(CancellationToken cancellationToken)
{
throw new System.NotImplementedException();
}
}

View file

@ -0,0 +1,27 @@
using System.Threading;
using System.Threading.Tasks;
using Elsa.Activities.Scheduling.Contracts;
using Elsa.Models;
namespace Elsa.Activities.Scheduling.Jobs;
public class RunWorkflowJob : IJob
{
public RunWorkflowJob()
{
}
public RunWorkflowJob(WorkflowIdentity workflowIdentity)
{
WorkflowIdentity = workflowIdentity;
}
public string JobId => $"workflow:{WorkflowIdentity.DefinitionId}";
public WorkflowIdentity WorkflowIdentity { get; init; } = default!;
public Task ExecuteAsync(CancellationToken cancellationToken)
{
throw new System.NotImplementedException();
}
}

View file

@ -0,0 +1,8 @@
using Elsa.Activities.Scheduling.Contracts;
namespace Elsa.Activities.Scheduling.Schedules;
public class CronSchedule : ISchedule
{
public string CronExpression { get; set; } = default!;
}

View file

@ -0,0 +1,10 @@
using System;
using Elsa.Activities.Scheduling.Contracts;
namespace Elsa.Activities.Scheduling.Schedules;
public class RecurringSchedule : ISchedule
{
public DateTime StartAt { get; set; }
public TimeSpan Interval { get; set; }
}

View file

@ -0,0 +1,9 @@
using System;
using Elsa.Activities.Scheduling.Contracts;
namespace Elsa.Activities.Scheduling.Schedules;
public class SpecificInstantSchedule : ISchedule
{
public DateTime DateTime { get; set; }
}

View file

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using Elsa.Attributes;
using Elsa.Contracts;
using Elsa.Models;
namespace Elsa.Activities.Scheduling;
@ -7,4 +9,12 @@ namespace Elsa.Activities.Scheduling;
public class Timer : Trigger
{
[Input] public Input<TimeSpan> Interval { get; set; } = default!;
protected override IEnumerable<object> GetHashInputs(TriggerIndexingContext context)
{
var interval = context.ExpressionExecutionContext.Get(Interval);
var clock = context.ExpressionExecutionContext.GetRequiredService<ISystemClock>();
var executeAt = clock.UtcNow.Add(interval);
return new object[] { executeAt, interval };
}
}

View file

@ -4,6 +4,5 @@ namespace Elsa.Contracts;
public interface ITrigger : INode
{
string TriggerType { get; set; }
ValueTask<IEnumerable<object>> GetHashInputsAsync(TriggerIndexingContext context, CancellationToken cancellationToken = default);
}

View file

@ -47,18 +47,35 @@ public class ActivityExecutionContext
ScheduleActivity(activity, completionCallback);
}
public void SetBookmarks(IEnumerable<object> hashInputs, IDictionary<string, object?>? data = default, ExecuteActivityDelegate? callback = default)
{
foreach (var hashInput in hashInputs)
SetBookmark(hashInput, data, callback);
}
public void SetBookmarks(IEnumerable<Bookmark> bookmarks) => _bookmarks.AddRange(bookmarks);
public void SetBookmark(Bookmark bookmark) => _bookmarks.Add(bookmark);
public void SetBookmark(string? hash, IDictionary<string, object?>? data = default, ExecuteActivityDelegate? callback = default) =>
public void SetBookmark(object? hashInput, IDictionary<string, object?>? data = default, ExecuteActivityDelegate? callback = default)
{
var hasher = GetRequiredService<IHasher>();
var hash = hashInput != null ? hasher.Hash(hashInput) : default;
SetBookmark(hash, data, callback);
}
public void SetBookmark(string? hash, IDictionary<string, object?>? data = default, ExecuteActivityDelegate? callback = default)
{
var identityGenerator = GetRequiredService<IIdentityGenerator>();
SetBookmark(new Bookmark(
Guid.NewGuid().ToString(),
identityGenerator.GenerateId(),
Activity.NodeType,
hash,
Activity.Id,
Id,
data ?? new Dictionary<string, object?>(),
callback?.Method.Name));
}
public T? GetProperty<T>(string key) => Properties.TryGetValue(key, out var value) ? (T?)value : default;
public void SetProperty<T>(string key, T value) => Properties[key] = value;

View file

@ -1,9 +1,14 @@
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Models;
public class ExpressionExecutionContext
{
public ExpressionExecutionContext(Register register, ExpressionExecutionContext? parentContext)
private readonly IServiceProvider _serviceProvider;
public ExpressionExecutionContext(IServiceProvider serviceProvider, Register register, ExpressionExecutionContext? parentContext)
{
_serviceProvider = serviceProvider;
Register = register;
ParentContext = parentContext;
}
@ -31,5 +36,6 @@ public class ExpressionExecutionContext
Set(output.LocationReference, convertedValue);
}
public T GetRequiredService<T>() where T : notnull => _serviceProvider.GetRequiredService<T>();
private RegisterLocation? GetLocationInternal(RegisterLocationReference locationReference) => Register.TryGetLocation(locationReference.Id, out var location) ? location : ParentContext?.GetLocationInternal(locationReference);
}

View file

@ -1,22 +1,16 @@
using Elsa.Contracts;
using Elsa.Helpers;
namespace Elsa.Models;
public class Trigger : Activity, ITrigger
public class Trigger : ITrigger
{
protected Trigger()
{
}
protected Trigger() => NodeType = TypeNameHelper.GenerateTypeName(GetType());
protected Trigger(string triggerType) => NodeType = triggerType;
protected Trigger(string triggerType) : base(triggerType)
{
}
public string TriggerType
{
get => NodeType;
set => NodeType = value;
}
public string Id { get; set; } = default!;
public string NodeType { get; set; }
public IDictionary<string, object> Metadata { get; set; } = new Dictionary<string, object>();
public virtual ValueTask<IEnumerable<object>> GetHashInputsAsync(TriggerIndexingContext context, CancellationToken cancellationToken = default)
{

View file

@ -0,0 +1,28 @@
using Elsa.Contracts;
namespace Elsa.Models;
public class TriggerActivity : Activity, ITrigger
{
protected TriggerActivity()
{
}
protected TriggerActivity(string triggerType) : base(triggerType)
{
}
public string TriggerType
{
get => NodeType;
set => NodeType = value;
}
public virtual ValueTask<IEnumerable<object>> GetHashInputsAsync(TriggerIndexingContext context, CancellationToken cancellationToken = default)
{
var hashes = GetHashInputs(context);
return ValueTask.FromResult(hashes);
}
protected virtual IEnumerable<object> GetHashInputs(TriggerIndexingContext context) => Enumerable.Empty<object>();
}

View file

@ -6,10 +6,12 @@ namespace Elsa.Services;
public class ActivityInvoker : IActivityInvoker
{
private readonly IActivityExecutionPipeline _pipeline;
private readonly IServiceProvider _serviceProvider;
public ActivityInvoker(IActivityExecutionPipeline pipeline)
public ActivityInvoker(IActivityExecutionPipeline pipeline, IServiceProvider serviceProvider)
{
_pipeline = pipeline;
_serviceProvider = serviceProvider;
}
public async Task InvokeAsync(
@ -25,7 +27,7 @@ public class ActivityInvoker : IActivityInvoker
// Setup an activity execution context.
var register = new Register();
var expressionExecutionContext = new ExpressionExecutionContext(register, parentActivityExecutionContext?.ExpressionExecutionContext);
var expressionExecutionContext = new ExpressionExecutionContext(_serviceProvider, register, parentActivityExecutionContext?.ExpressionExecutionContext);
var activityExecutionContext = new ActivityExecutionContext(workflowExecutionContext, parentActivityExecutionContext, expressionExecutionContext, activity, cancellationToken);
// Declare locations.

View file

@ -9,6 +9,13 @@ namespace Elsa.Services;
public class WorkflowStateSerializer : IWorkflowStateSerializer
{
private readonly IServiceProvider _serviceProvider;
public WorkflowStateSerializer(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public WorkflowState ReadState(WorkflowExecutionContext workflowExecutionContext)
{
var state = new WorkflowState
@ -119,7 +126,7 @@ public class WorkflowStateSerializer : IWorkflowStateSerializer
{
var activity = workflowExecutionContext.FindActivityById(activityExecutionContextState.ScheduledActivityId);
var register = new Register(activityExecutionContextState.Register.Locations);
var expressionExecutionContext = new ExpressionExecutionContext(register, default);
var expressionExecutionContext = new ExpressionExecutionContext(_serviceProvider, register, default);
var properties = activityExecutionContextState.Properties;
var activityExecutionContext = new ActivityExecutionContext(workflowExecutionContext, default, expressionExecutionContext, activity, workflowExecutionContext.CancellationToken)
{

View file

@ -45,9 +45,9 @@ public class TriggerDescriber : ITriggerDescriber
InputProperties = DescribeInputProperties(inputProperties).ToList(),
Constructor = context =>
{
var activity = _activityFactory.Create(triggerType, context);
activity.TriggerType = fullTypeName;
return activity;
var trigger = _activityFactory.Create(triggerType, context);
trigger.NodeType = fullTypeName;
return trigger;
}
};

View file

@ -0,0 +1,10 @@
using Elsa.Activities.Scheduling.Contracts;
using IElsaJob = Elsa.Activities.Scheduling.Contracts.IJob;
namespace Elsa.Modules.Quartz.Contracts;
public interface IElsaJobSerializer
{
string Serialize(IJob job);
T Deserialize<T>(string json) where T : IElsaJob;
}

View file

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Quartz" Version="3.3.3" />
<PackageReference Include="Quartz.Extensions.DependencyInjection" Version="3.3.3" />
<PackageReference Include="Quartz.Extensions.Hosting" Version="3.3.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\activities\Elsa.Activities.Scheduling\Elsa.Activities.Scheduling.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,74 @@
using Elsa.Activities.Scheduling.Contracts;
using Elsa.Activities.Scheduling.Jobs;
using Elsa.Modules.Quartz.Contracts;
using Elsa.Modules.Quartz.Jobs;
using Elsa.Modules.Quartz.Services;
using Microsoft.Extensions.DependencyInjection;
using Quartz;
using IElsaJob = Elsa.Activities.Scheduling.Contracts.IJob;
namespace Elsa.Modules.Quartz.Extensions;
public static class ServiceCollectionExtensions
{
/// <summary>
/// This will register both Quartz as well as Elsa-specific services and jobs.
/// If you prefer to register Quartz yourself, use <see cref="ConfigureQuartzModule"/>
/// </summary>
public static IServiceCollection AddQuartzModule(
this IServiceCollection services,
Action<QuartzOptions>? configureQuartzOptions = default,
Action<IServiceCollectionQuartzConfigurator>? configureQuartz = default,
Action<QuartzHostedServiceOptions>? configureQuartzHostedService = default)
{
if (configureQuartzOptions != null)
services.Configure(configureQuartzOptions);
return services
.AddQuartz(configure =>
{
ConfigureQuartz(configure, configureQuartz);
ConfigureQuartzModule(services, configure);
})
.AddQuartzHostedService(options => ConfigureQuartzHostedService(options, configureQuartzHostedService));
}
/// <summary>
/// This will register Elsa-specific services and jobs, but will **not** register Quartz itself. To register Quartz, you need to do so yourself, or use <see cref="AddQuartzModule"/> to register & configure Quartz for Elsa.
/// </summary>
public static IServiceCollection ConfigureQuartzModule(this IServiceCollection services, IServiceCollectionQuartzConfigurator quartz)
{
services
.AddSingleton<IElsaJobSerializer, ElsaJobSerializer>()
.AddSingleton<IJobScheduler, QuartzJobScheduler>();
quartz.AddElsaJobs();
return services;
}
private static IServiceCollectionQuartzConfigurator AddElsaJobs(this IServiceCollectionQuartzConfigurator quartz)
{
quartz.AddJob<RunWorkflowJob>();
return quartz;
}
private static void ConfigureQuartzHostedService(QuartzHostedServiceOptions options, Action<QuartzHostedServiceOptions>? configureQuartzHostedService)
{
options.WaitForJobsToComplete = true;
configureQuartzHostedService?.Invoke(options);
}
private static IServiceCollectionQuartzConfigurator AddJob<TJob>(this IServiceCollectionQuartzConfigurator quartz) where TJob : IElsaJob =>
quartz.AddJob<QuartzJob<RunWorkflowJob>>(job => job.StoreDurably().WithIdentity(nameof(RunWorkflowJob)));
private static void ConfigureQuartz(IServiceCollectionQuartzConfigurator quartz, Action<IServiceCollectionQuartzConfigurator>? configureQuartz)
{
quartz.UseMicrosoftDependencyInjectionJobFactory();
quartz.AddJob<QuartzJob<RunWorkflowJob>>(job => job.StoreDurably().WithIdentity(nameof(RunWorkflowJob)));
quartz.UseSimpleTypeLoader();
quartz.UseInMemoryStore();
configureQuartz?.Invoke(quartz);
}
}

View file

@ -0,0 +1,25 @@
using Elsa.Modules.Quartz.Contracts;
using Elsa.Modules.Quartz.Services;
using Quartz;
using IElsaJob = Elsa.Activities.Scheduling.Contracts.IJob;
namespace Elsa.Modules.Quartz.Jobs;
/// <summary>
/// A generic Quartz job that executes Elsa scheduled jobs.
/// </summary>
/// <typeparam name="TElsaJob"></typeparam>
public class QuartzJob<TElsaJob> : IJob where TElsaJob : IElsaJob
{
private readonly IElsaJobSerializer _elsaJobSerializer;
public QuartzJob(IElsaJobSerializer elsaJobSerializer) => _elsaJobSerializer = elsaJobSerializer;
public async Task Execute(IJobExecutionContext context)
{
var json = context.MergedJobDataMap.GetString(QuartzJobScheduler.JobDataKey)!;
var elsaJob = _elsaJobSerializer.Deserialize<TElsaJob>(json);
await elsaJob.ExecuteAsync(context.CancellationToken);
}
}

View file

@ -0,0 +1,23 @@
using System.Text.Json;
using Elsa.Activities.Scheduling.Contracts;
using Elsa.Modules.Quartz.Contracts;
using IElsaJob = Elsa.Activities.Scheduling.Contracts.IJob;
namespace Elsa.Modules.Quartz.Services;
public class ElsaJobSerializer : IElsaJobSerializer
{
public string Serialize(IJob job)
{
var serializerOptions = CreateSerializerOptions();
return JsonSerializer.Serialize(job, serializerOptions);
}
public T Deserialize<T>(string json) where T : IElsaJob
{
var serializerOptions = CreateSerializerOptions();
return JsonSerializer.Deserialize<T>(json, serializerOptions)!;
}
private JsonSerializerOptions CreateSerializerOptions() => new();
}

View file

@ -0,0 +1,77 @@
using Elsa.Activities.Scheduling.Schedules;
using Elsa.Modules.Quartz.Contracts;
using Elsa.Modules.Quartz.Jobs;
using Microsoft.Extensions.Logging;
using Quartz;
using IElsaJobScheduler = Elsa.Activities.Scheduling.Contracts.IJobScheduler;
using IElsaJob = Elsa.Activities.Scheduling.Contracts.IJob;
using IElsaSchedule = Elsa.Activities.Scheduling.Contracts.ISchedule;
namespace Elsa.Modules.Quartz.Services;
public class QuartzJobScheduler : IElsaJobScheduler
{
public const string JobDataKey = "ElsaJob";
private readonly IElsaJobSerializer _elsaJobSerializer;
private readonly ISchedulerFactory _schedulerFactory;
private readonly ILogger _logger;
public QuartzJobScheduler(IElsaJobSerializer elsaJobSerializer, ISchedulerFactory schedulerFactory, ILogger<QuartzJobScheduler> logger)
{
_elsaJobSerializer = elsaJobSerializer;
_schedulerFactory = schedulerFactory;
_logger = logger;
}
public async Task ScheduleAsync(IElsaJob job, IElsaSchedule schedule, CancellationToken cancellationToken = default)
{
var quartzTrigger = CreateTrigger(job, schedule);
await ScheduleJob(quartzTrigger, cancellationToken);
}
private async Task ScheduleJob(ITrigger trigger, CancellationToken cancellationToken)
{
var scheduler = await _schedulerFactory.GetScheduler(cancellationToken);
try
{
await scheduler.ScheduleJob(trigger, cancellationToken);
}
catch (SchedulerException e)
{
_logger.LogWarning(e, "Failed to schedule trigger {TriggerKey}", trigger.Key.ToString());
}
}
private ITrigger CreateTrigger(IElsaJob job, IElsaSchedule schedule)
{
var jobName = job.GetType().Name;
var json = _elsaJobSerializer.Serialize(job);
var builder = TriggerBuilder.Create().ForJob(jobName).WithIdentity(job.JobId).UsingJobData(JobDataKey, json);
switch (schedule)
{
case RecurringSchedule recurringSchedule:
{
builder.StartAt(recurringSchedule.StartAt);
builder.WithSimpleSchedule(x => x.WithInterval(recurringSchedule.Interval).RepeatForever());
break;
}
case CronSchedule cronSchedule:
{
builder.WithCronSchedule(cronSchedule.CronExpression);
break;
}
case SpecificInstantSchedule specificInstantSchedule:
{
builder.StartAt(specificInstantSchedule.DateTime);
break;
}
default:
throw new NotSupportedException($"Schedule of type {schedule.GetType()} is not supported. But if you create an issue, we'll make this logic extensible & replaceable :)");
}
return builder.Build();
}
}

View file

@ -17,6 +17,7 @@ public class TriggerIndexer : ITriggerIndexer
private readonly IWorkflowRegistry _workflowRegistry;
private readonly IExpressionEvaluator _expressionEvaluator;
private readonly ICommandSender _mediator;
private readonly IServiceProvider _serviceProvider;
private readonly IHasher _hasher;
private readonly ILogger _logger;
@ -24,12 +25,14 @@ public class TriggerIndexer : ITriggerIndexer
IWorkflowRegistry workflowRegistry,
IExpressionEvaluator expressionEvaluator,
ICommandSender mediator,
IServiceProvider serviceProvider,
IHasher hasher,
ILogger<TriggerIndexer> logger)
{
_workflowRegistry = workflowRegistry;
_expressionEvaluator = expressionEvaluator;
_mediator = mediator;
_serviceProvider = serviceProvider;
_hasher = hasher;
_logger = logger;
}
@ -79,7 +82,7 @@ public class TriggerIndexer : ITriggerIndexer
var inputs = trigger.GetInputs();
var assignedInputs = inputs.Where(x => x.LocationReference != null!).ToList();
var register = context.GetOrCreateRegister(trigger);
var expressionExecutionContext = new ExpressionExecutionContext(register, default);
var expressionExecutionContext = new ExpressionExecutionContext(_serviceProvider, register, default);
// Evaluate trigger inputs.
foreach (var input in assignedInputs)
@ -121,9 +124,9 @@ public class TriggerIndexer : ITriggerIndexer
}
catch (Exception e)
{
_logger.LogWarning( e, "Failed to get hash inputs");
_logger.LogWarning(e, "Failed to get hash inputs");
}
return Array.Empty<object>() ;
return Array.Empty<object>();
}
}

View file

@ -7,8 +7,10 @@
<ItemGroup>
<ProjectReference Include="..\..\..\activities\Elsa.Activities.Http\Elsa.Activities.Http.csproj" />
<ProjectReference Include="..\..\..\activities\Elsa.Activities.Scheduling\Elsa.Activities.Scheduling.csproj" />
<ProjectReference Include="..\..\..\api\Elsa.Api\Elsa.Api.csproj" />
<ProjectReference Include="..\..\..\core\Elsa\Elsa.csproj" />
<ProjectReference Include="..\..\..\modules\Elsa.Modules.Quartz\Elsa.Modules.Quartz.csproj" />
<ProjectReference Include="..\..\..\persistence\Elsa.Persistence.EntityFrameworkCore.Sqlite\Elsa.Persistence.EntityFrameworkCore.Sqlite.csproj" />
<ProjectReference Include="..\..\..\runtime\Elsa.Runtime.ProtoActor\Elsa.Runtime.ProtoActor.csproj" />
<ProjectReference Include="..\..\..\scripting\Elsa.Scripting.Liquid\Elsa.Scripting.Liquid.csproj" />

View file

@ -2,12 +2,14 @@ using Elsa.Activities.Console;
using Elsa.Activities.ControlFlow;
using Elsa.Activities.Http;
using Elsa.Activities.Http.Extensions;
using Elsa.Activities.Scheduling;
using Elsa.Activities.Workflows;
using Elsa.Api.Extensions;
using Elsa.Extensions;
using Elsa.Management.Contracts;
using Elsa.Management.Extensions;
using Elsa.Mediator.Extensions;
using Elsa.Modules.Quartz.Extensions;
using Elsa.Persistence.EntityFrameworkCore.Extensions;
using Elsa.Persistence.EntityFrameworkCore.Sqlite;
using Elsa.Persistence.Middleware.WorkflowExecution;
@ -52,17 +54,22 @@ services
.AddActivity<ReadLine>()
.AddActivity<If>()
.AddActivity<HttpTrigger>()
.AddActivity<Flowchart>();
.AddActivity<Flowchart>()
;
// Register available triggers.
services
.AddTrigger<HttpTrigger>();
.AddTrigger<HttpTrigger>()
.AddTrigger<Timer>();
// Register scripting languages.
services
.AddJavaScriptExpressions()
.AddLiquidExpressions();
// Register modules.
services.AddQuartzModule(); // Provides a scheduler implementation for Timer activities.
// Configure middleware pipeline.
var app = builder.Build();
var serviceProvider = app.Services;