diff --git a/src/core/Elsa.Core/Services/Workflows/WorkflowRunner.cs b/src/core/Elsa.Core/Services/Workflows/WorkflowRunner.cs index b13925d6e..131022e0a 100644 --- a/src/core/Elsa.Core/Services/Workflows/WorkflowRunner.cs +++ b/src/core/Elsa.Core/Services/Workflows/WorkflowRunner.cs @@ -102,8 +102,11 @@ namespace Elsa.Services.Workflows if (!runWorkflowResult.Executed) { - _logger.LogDebug("Workflow {WorkflowInstanceId} cannot begin from an idle state (perhaps it needs a specific input)", workflowInstance.Id); - return runWorkflowResult; + if (workflowInstance.WorkflowStatus != WorkflowStatus.Faulted) + { + _logger.LogDebug("Workflow {WorkflowInstanceId} cannot begin from an idle state (perhaps it needs a specific input)", workflowInstance.Id); + return runWorkflowResult; + } } break; @@ -140,7 +143,6 @@ namespace Elsa.Services.Workflows if (statusEvent != null) { - _logger.LogTrace("Publishing a status event of type {EventType} for workflow {WorkflowInstanceId}", statusEvent.GetType().Name, workflowInstance.Id); await _mediator.Publish(statusEvent, cancellationToken); } @@ -153,13 +155,23 @@ namespace Elsa.Services.Workflows if (activity == null) activity = _startingActivitiesProvider.GetStartActivities(workflowExecutionContext.WorkflowBlueprint).FirstOrDefault() ?? workflowExecutionContext.WorkflowBlueprint.Activities.First(); - if (!await CanExecuteAsync(workflowExecutionContext, activity, false, cancellationToken)) - return new RunWorkflowResult(workflowExecutionContext.WorkflowInstance, activity.Id, false); + try + { + if (!await CanExecuteAsync(workflowExecutionContext, activity, false, cancellationToken)) + return new RunWorkflowResult(workflowExecutionContext.WorkflowInstance, activity.Id, false); + + workflowExecutionContext.Begin(); + workflowExecutionContext.ScheduleActivity(activity.Id); + await RunAsync(workflowExecutionContext, Execute, cancellationToken); + return new RunWorkflowResult(workflowExecutionContext.WorkflowInstance, activity.Id, true); + } + catch (Exception e) + { + _logger.LogWarning(e, "Failed to run workflow {WorkflowInstanceId}", workflowExecutionContext.WorkflowInstance.Id); + workflowExecutionContext.Fault(e, null, null, false); + } - workflowExecutionContext.Begin(); - workflowExecutionContext.ScheduleActivity(activity.Id); - await RunAsync(workflowExecutionContext, Execute, cancellationToken); - return new RunWorkflowResult(workflowExecutionContext.WorkflowInstance, activity.Id, true); + return new RunWorkflowResult(workflowExecutionContext.WorkflowInstance, activity.Id, false); } private async Task RunWorkflowAsync(WorkflowExecutionContext workflowExecutionContext, CancellationToken cancellationToken) diff --git a/src/samples/server/Elsa.Samples.Server.Host/Startup.cs b/src/samples/server/Elsa.Samples.Server.Host/Startup.cs index ddfbed3f1..fa9642239 100644 --- a/src/samples/server/Elsa.Samples.Server.Host/Startup.cs +++ b/src/samples/server/Elsa.Samples.Server.Host/Startup.cs @@ -70,10 +70,6 @@ namespace Elsa.Samples.Server.Host .ConfigureWorkflowChannels(options => elsaSection.GetSection("WorkflowChannels").Bind(options)) ); - // Hangfire. - services.AddHangfire(configuration => configuration.UseSqlServerStorage(Configuration.GetConnectionString("SqlServer"))); - services.AddHangfireServer((sp, options)=> options.ConfigureForElsaDispatchers(sp)); - // Elsa API endpoints. services .AddElsaApiEndpoints() diff --git a/src/samples/server/Elsa.Samples.Server.Host/appsettings.json b/src/samples/server/Elsa.Samples.Server.Host/appsettings.json index 253fd70a1..559042838 100644 --- a/src/samples/server/Elsa.Samples.Server.Host/appsettings.json +++ b/src/samples/server/Elsa.Samples.Server.Host/appsettings.json @@ -24,7 +24,7 @@ "Framework": "EntityFrameworkCore", "ConnectionStringIdentifier": "Sqlite" }, - "DispatcherHangfire": true, + "DispatcherHangfire": false, "Console": true, "Http": true, "Email": true, diff --git a/src/server/Elsa.Server.Api/Endpoints/Workflows/Dispatch.cs b/src/server/Elsa.Server.Api/Endpoints/Workflows/Dispatch.cs index e3c9b8fa6..525faaf9d 100644 --- a/src/server/Elsa.Server.Api/Endpoints/Workflows/Dispatch.cs +++ b/src/server/Elsa.Server.Api/Endpoints/Workflows/Dispatch.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc; using Swashbuckle.AspNetCore.Annotations; using System.Threading; using System.Threading.Tasks; +using Elsa.Models; namespace Elsa.Server.Api.Endpoints.Workflows { @@ -41,7 +42,7 @@ namespace Elsa.Server.Api.Endpoints.Workflows if (startableWorkflow == null) return NotFound(); - var result = await _workflowLaunchpad.DispatchStartableWorkflowAsync(startableWorkflow, request.Input, cancellationToken); + var result = await _workflowLaunchpad.DispatchStartableWorkflowAsync(startableWorkflow, new WorkflowInput(request.Input), cancellationToken); return Ok(new DispatchWorkflowDefinitionResponseModel(result.WorkflowInstanceId, result.ActivityId)); } } diff --git a/src/server/Elsa.Server.Api/Endpoints/Workflows/Execute.cs b/src/server/Elsa.Server.Api/Endpoints/Workflows/Execute.cs index eede5af8d..f9c0ab630 100644 --- a/src/server/Elsa.Server.Api/Endpoints/Workflows/Execute.cs +++ b/src/server/Elsa.Server.Api/Endpoints/Workflows/Execute.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc; using Swashbuckle.AspNetCore.Annotations; using System.Threading; using System.Threading.Tasks; +using Elsa.Models; namespace Elsa.Server.Api.Endpoints.Workflows { @@ -41,7 +42,7 @@ namespace Elsa.Server.Api.Endpoints.Workflows if (startableWorkflow == null) return NotFound(); - var result = await _workflowLaunchpad.ExecuteStartableWorkflowAsync(startableWorkflow, request.Input, cancellationToken); + var result = await _workflowLaunchpad.ExecuteStartableWorkflowAsync(startableWorkflow, new WorkflowInput(request.Input), cancellationToken); if (Response.HasStarted) return new EmptyResult(); diff --git a/src/server/Elsa.Server.Api/Endpoints/Workflows/Models.cs b/src/server/Elsa.Server.Api/Endpoints/Workflows/Models.cs index 4775daf54..e5f73c480 100644 --- a/src/server/Elsa.Server.Api/Endpoints/Workflows/Models.cs +++ b/src/server/Elsa.Server.Api/Endpoints/Workflows/Models.cs @@ -6,20 +6,20 @@ using Elsa.Services.Models; namespace Elsa.Server.Api.Endpoints.Workflows { - public record ExecuteWorkflowDefinitionRequestModel(string? ActivityId, string? CorrelationId, string? ContextId, WorkflowInput? Input); + public record ExecuteWorkflowDefinitionRequestModel(string? ActivityId, string? CorrelationId, string? ContextId, object? Input); public record ExecuteWorkflowDefinitionResponseModel(bool Executed, string? ActivityId, WorkflowInstance? WorkflowInstance); - public record DispatchWorkflowDefinitionRequestModel(string? ActivityId, string? CorrelationId, string? ContextId, WorkflowInput? Input); + public record DispatchWorkflowDefinitionRequestModel(string? ActivityId, string? CorrelationId, string? ContextId, object? Input); public record DispatchWorkflowDefinitionResponseModel(string WorkflowInstanceId, string? ActivityId); public record TriggeredWorkflowModel(string WorkflowInstanceId, string? ActivityId); - public record DispatchTriggerWorkflowsRequestModel(string ActivityType, IBookmark? Bookmark, IBookmark? Trigger, string? CorrelationId, string? WorkflowInstanceId, string? ContextId, WorkflowInput? Input); + public record DispatchTriggerWorkflowsRequestModel(string ActivityType, IBookmark? Bookmark, IBookmark? Trigger, string? CorrelationId, string? WorkflowInstanceId, string? ContextId, object? Input); public record DispatchTriggerWorkflowsResponseModel(ICollection PendingWorkflows); - public record TriggerWorkflowsRequestModel(string ActivityType, IBookmark? Bookmark, string? CorrelationId, string? WorkflowInstanceId, string? ContextId, WorkflowInput? Input, bool Dispatch); + public record TriggerWorkflowsRequestModel(string ActivityType, IBookmark? Bookmark, string? CorrelationId, string? WorkflowInstanceId, string? ContextId, object? Input, bool Dispatch); public record TriggerWorkflowsResponseModel(ICollection TriggeredWorkflows); } \ No newline at end of file diff --git a/src/server/Elsa.Server.Api/Endpoints/Workflows/Trigger.cs b/src/server/Elsa.Server.Api/Endpoints/Workflows/Trigger.cs index ca9404e42..ee50ac0af 100644 --- a/src/server/Elsa.Server.Api/Endpoints/Workflows/Trigger.cs +++ b/src/server/Elsa.Server.Api/Endpoints/Workflows/Trigger.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Elsa.Models; using Elsa.Server.Api.ActionFilters; using Elsa.Services; using Elsa.Services.Models; @@ -44,12 +45,12 @@ namespace Elsa.Server.Api.Endpoints.Workflows if (request.Dispatch) { - var result = await _workflowLaunchpad.CollectAndDispatchWorkflowsAsync(context, request.Input, cancellationToken).ToList(); + var result = await _workflowLaunchpad.CollectAndDispatchWorkflowsAsync(context, new WorkflowInput(request.Input), cancellationToken).ToList(); triggeredWorkflows = result.Select(x => new TriggeredWorkflowModel(x.WorkflowInstanceId, x.ActivityId)).ToList(); } else { - var result = await _workflowLaunchpad.CollectAndExecuteWorkflowsAsync(context, request.Input, cancellationToken).ToList(); + var result = await _workflowLaunchpad.CollectAndExecuteWorkflowsAsync(context, new WorkflowInput(request.Input), cancellationToken).ToList(); triggeredWorkflows = result.Select(x => new TriggeredWorkflowModel(x.WorkflowInstanceId, x.ActivityId)).ToList(); } diff --git a/src/server/Elsa.Server.Api/Services/WorkflowBlueprintMapper.cs b/src/server/Elsa.Server.Api/Services/WorkflowBlueprintMapper.cs index 7e198704f..c0d221568 100644 --- a/src/server/Elsa.Server.Api/Services/WorkflowBlueprintMapper.cs +++ b/src/server/Elsa.Server.Api/Services/WorkflowBlueprintMapper.cs @@ -1,7 +1,9 @@ -using System.Linq; +using System; +using System.Linq; using System.Threading; using System.Threading.Tasks; using AutoMapper; +using Elsa.Exceptions; using Elsa.Metadata; using Elsa.Models; using Elsa.Server.Api.Endpoints.WorkflowRegistry; @@ -68,10 +70,22 @@ namespace Elsa.Server.Api.Services return (inputProperties, outputProperties); } - private async Task GetPropertyValueAsync(IWorkflowBlueprint workflowBlueprint, IActivityBlueprintWrapper activityBlueprintWrapper, ActivityInputDescriptor propertyDescriptor, CancellationToken cancellationToken) => - // Ony evaluate designer-critical properties, as they are necessary for proper designer display such as dynamic outcomes. These evaluations must not fail. - propertyDescriptor.IsDesignerCritical - ? await activityBlueprintWrapper.EvaluatePropertyValueAsync(propertyDescriptor.Name, cancellationToken) - : workflowBlueprint.GetActivityPropertyRawValue(activityBlueprintWrapper.ActivityBlueprint.Id, propertyDescriptor.Name); + private static async Task GetPropertyValueAsync(IWorkflowBlueprint workflowBlueprint, IActivityBlueprintWrapper activityBlueprintWrapper, ActivityInputDescriptor propertyDescriptor, CancellationToken cancellationToken) + { + if (propertyDescriptor.IsDesignerCritical) + { + try + { + return await activityBlueprintWrapper.EvaluatePropertyValueAsync(propertyDescriptor.Name, cancellationToken); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new WorkflowException("Failed to evaluate a designer-critical property value. Please make sure that the value does not rely on external context.", e); + } + } + + return workflowBlueprint.GetActivityPropertyRawValue(activityBlueprintWrapper.ActivityBlueprint.Id, propertyDescriptor.Name); + } } } \ No newline at end of file