Implement activity execution logging (#89)
* Implement activity logging * Update Calculator example
This commit is contained in:
parent
bdbede4e1b
commit
012e12c51c
|
|
@ -13,13 +13,13 @@ namespace Sample04
|
|||
public void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder
|
||||
.StartWith<WriteLine>(x => x.TextExpression = new LiteralExpression("Welcome to Calculator Workflow!"))
|
||||
.Then<WriteLine>(x => x.TextExpression = new LiteralExpression("Enter number 1:"), id: "start")
|
||||
.Then<ReadLine>(x => x.VariableName = "number1")
|
||||
.Then<WriteLine>(x => x.TextExpression = new LiteralExpression("Enter number 2:"))
|
||||
.Then<ReadLine>(x => x.VariableName = "number2")
|
||||
.Then<WriteLine>(x => x.TextExpression = new LiteralExpression("Now enter the operation you wish to apply. Options are: add, subtract, multiply or divide:"))
|
||||
.Then<ReadLine>(x => x.VariableName = "operation")
|
||||
.StartWith<WriteLine>(x => x.TextExpression = new LiteralExpression("Welcome to Calculator Workflow!"), "welcome")
|
||||
.Then<WriteLine>(x => x.TextExpression = new LiteralExpression("Enter number 1:"), id: "enter-first-number-prompt")
|
||||
.Then<ReadLine>(x => x.VariableName = "number1", id: "read-first-number")
|
||||
.Then<WriteLine>(x => x.TextExpression = new LiteralExpression("Enter number 2:"), id: "enter-second-number-prompt")
|
||||
.Then<ReadLine>(x => x.VariableName = "number2", id: "read-second-number")
|
||||
.Then<WriteLine>(x => x.TextExpression = new LiteralExpression("Now enter the operation you wish to apply. Options are: add, subtract, multiply or divide:"), id: "enter-operation-prompt")
|
||||
.Then<ReadLine>(x => x.VariableName = "operation", id: "read-operation")
|
||||
.Then<Switch>(@switch =>
|
||||
{
|
||||
@switch.Expression = new JavaScriptExpression<string>("operation");
|
||||
|
|
@ -29,40 +29,41 @@ namespace Sample04
|
|||
{
|
||||
@switch
|
||||
.When("add")
|
||||
.Then<Sum>(SetupOperation)
|
||||
.Then("showResult");
|
||||
.Then<Sum>(SetupOperation, id: "perform-sum")
|
||||
.Then("show-result");
|
||||
|
||||
@switch
|
||||
.When("subtract")
|
||||
.Then<Subtract>(SetupOperation)
|
||||
.Then("showResult");
|
||||
.Then<Subtract>(SetupOperation, id: "perform-subtract")
|
||||
.Then("show-result");
|
||||
|
||||
@switch
|
||||
.When("multiply")
|
||||
.Then<Multiply>(SetupOperation)
|
||||
.Then("showResult");
|
||||
.Then<Multiply>(SetupOperation, id: "perform-multiply")
|
||||
.Then("show-result");
|
||||
|
||||
@switch
|
||||
.When("divide")
|
||||
.Then<Divide>(SetupOperation)
|
||||
.Then("showResult");
|
||||
}
|
||||
.Then<Divide>(SetupOperation, id: "perform-divide")
|
||||
.Then("show-result");
|
||||
},
|
||||
"inspect-selected-operation"
|
||||
)
|
||||
.Add<WriteLine>(x => x.TextExpression = new JavaScriptExpression<string>("`Result: ${result}`"), "showResult")
|
||||
.Then<WriteLine>(x => x.TextExpression = new LiteralExpression("Try again? (y/n)"))
|
||||
.Then<ReadLine>(x => x.VariableName = "retry")
|
||||
.Add<WriteLine>(x => x.TextExpression = new JavaScriptExpression<string>("`Result: ${result}`"), "show-result")
|
||||
.Then<WriteLine>(x => x.TextExpression = new LiteralExpression("Try again? (y/n)"), id: "try-again-prompt")
|
||||
.Then<ReadLine>(x => x.VariableName = "retry", id: "read-try-again")
|
||||
.Then<IfElse>(
|
||||
x => x.ConditionExpression = new JavaScriptExpression<bool>("retry.toLowerCase() === 'y'"),
|
||||
ifElse =>
|
||||
{
|
||||
ifElse
|
||||
.When(OutcomeNames.True)
|
||||
.Then("start");
|
||||
.Then("enter-first-number-prompt");
|
||||
|
||||
ifElse
|
||||
.When(OutcomeNames.False)
|
||||
.Then<WriteLine>(x => x.TextExpression = new LiteralExpression("Bye!"));
|
||||
});;
|
||||
.Then<WriteLine>(x => x.TextExpression = new LiteralExpression("Bye!"), id: "say-good-bye");
|
||||
}, id: "inspect-retry");
|
||||
}
|
||||
|
||||
private void SetupOperation(ArithmeticOperation operation)
|
||||
|
|
|
|||
|
|
@ -35,6 +35,11 @@ namespace Sample04
|
|||
var invoker = services.GetService<IWorkflowInvoker>();
|
||||
await invoker.StartAsync(workflow);
|
||||
|
||||
Console.WriteLine("Workflow has ended. Here are the activities that have executed:");
|
||||
foreach (var logEntry in workflow.ExecutionLog)
|
||||
{
|
||||
Console.WriteLine("{0}: {1}", logEntry.Timestamp, logEntry.ActivityId);
|
||||
}
|
||||
Console.ReadLine();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,11 @@ namespace Elsa.Services
|
|||
/// </summary>
|
||||
Task ActivityExecutedAsync(WorkflowExecutionContext workflowExecutionContext, IActivity activity, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when an activity has faulted.
|
||||
/// </summary>
|
||||
Task ActivityFaultedAsync(WorkflowExecutionContext workflowExecutionContext, IActivity activity, string message, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when halted activities are about to be executed.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -6,19 +6,36 @@ namespace Elsa.Services
|
|||
{
|
||||
public abstract class WorkflowEventHandlerBase : IWorkflowEventHandler
|
||||
{
|
||||
public virtual Task ActivityExecutedAsync(WorkflowExecutionContext workflowExecutionContext, IActivity activity, CancellationToken cancellationToken)
|
||||
public virtual Task ActivityExecutedAsync(
|
||||
WorkflowExecutionContext workflowExecutionContext,
|
||||
IActivity activity,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ActivityExecuted(workflowExecutionContext, activity);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public virtual Task InvokingHaltedActivitiesAsync(WorkflowExecutionContext workflowExecutionContext, CancellationToken cancellationToken)
|
||||
public virtual Task ActivityFaultedAsync(
|
||||
WorkflowExecutionContext workflowExecutionContext,
|
||||
IActivity activity,
|
||||
string message,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ActivityFaulted(workflowExecutionContext, activity, message);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public virtual Task InvokingHaltedActivitiesAsync(
|
||||
WorkflowExecutionContext workflowExecutionContext,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
InvokingHaltedActivities(workflowExecutionContext);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public virtual Task WorkflowInvokedAsync(WorkflowExecutionContext workflowExecutionContext, CancellationToken cancellationToken)
|
||||
public virtual Task WorkflowInvokedAsync(
|
||||
WorkflowExecutionContext workflowExecutionContext,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
WorkflowInvoked(workflowExecutionContext);
|
||||
return Task.CompletedTask;
|
||||
|
|
@ -27,11 +44,18 @@ namespace Elsa.Services
|
|||
protected virtual void ActivityExecuted(WorkflowExecutionContext workflowExecutionContext, IActivity activity)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
protected virtual void ActivityFaulted(
|
||||
WorkflowExecutionContext workflowExecutionContext,
|
||||
IActivity activity,
|
||||
string message)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void InvokingHaltedActivities(WorkflowExecutionContext workflowExecutionContext)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
protected virtual void WorkflowInvoked(WorkflowExecutionContext workflowExecutionContext)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ namespace Elsa.Activities.ControlFlow
|
|||
// For each inbound connection, record the transition.
|
||||
foreach (var inboundConnection in inboundConnections)
|
||||
{
|
||||
var joinActivity = (Join) inboundConnection.Target.Activity;
|
||||
var joinActivity = (Join)inboundConnection.Target.Activity;
|
||||
var inboundTransitions = joinActivity.InboundTransitions ?? new List<string>();
|
||||
joinActivity.InboundTransitions = inboundTransitions
|
||||
.Union(new[] { GetTransitionKey(inboundConnection) })
|
||||
|
|
@ -115,7 +115,9 @@ namespace Elsa.Activities.ControlFlow
|
|||
return $"@{sourceActivityId}_{sourceOutcomeName}";
|
||||
}
|
||||
|
||||
Task IWorkflowEventHandler.ActivityExecutedAsync(WorkflowExecutionContext workflowContext, IActivity activity,
|
||||
Task IWorkflowEventHandler.ActivityExecutedAsync(
|
||||
WorkflowExecutionContext workflowContext,
|
||||
IActivity activity,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
RecordInboundTransitions(workflowContext, activity);
|
||||
|
|
@ -123,10 +125,18 @@ namespace Elsa.Activities.ControlFlow
|
|||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
Task IWorkflowEventHandler.InvokingHaltedActivitiesAsync(WorkflowExecutionContext workflowExecutionContext,
|
||||
public Task ActivityFaultedAsync(
|
||||
WorkflowExecutionContext workflowExecutionContext,
|
||||
IActivity activity,
|
||||
string message,
|
||||
CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
||||
Task IWorkflowEventHandler.WorkflowInvokedAsync(WorkflowExecutionContext workflowExecutionContext,
|
||||
Task IWorkflowEventHandler.InvokingHaltedActivitiesAsync(
|
||||
WorkflowExecutionContext workflowExecutionContext,
|
||||
CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
||||
Task IWorkflowEventHandler.WorkflowInvokedAsync(
|
||||
WorkflowExecutionContext workflowExecutionContext,
|
||||
CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ using Elsa.Activities.Workflows;
|
|||
using Elsa.AutoMapper.Extensions;
|
||||
using Elsa.Expressions;
|
||||
using Elsa.Mapping;
|
||||
using Elsa.Persistence;
|
||||
using Elsa.Persistence.Memory;
|
||||
using Elsa.Runtime;
|
||||
using Elsa.Scripting;
|
||||
|
|
@ -16,6 +17,7 @@ using Elsa.Services;
|
|||
using Elsa.Services.Models;
|
||||
using Elsa.StartupTasks;
|
||||
using Elsa.WorkflowBuilders;
|
||||
using Elsa.WorkflowEventHandlers;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using NodaTime;
|
||||
|
|
@ -61,6 +63,7 @@ namespace Elsa.Extensions
|
|||
.AddScoped<IScopedWorkflowInvoker, ScopedWorkflowInvoker>()
|
||||
.AddScoped<IActivityResolver, ActivityResolver>()
|
||||
.AddScoped<IWorkflowBuilder, WorkflowBuilder>()
|
||||
.AddScoped<IWorkflowEventHandler, ActivityLoggingWorkflowEventHandler>()
|
||||
.AddStartupTask<PopulateRegistryTask>()
|
||||
.AddSingleton<Func<IWorkflowBuilder>>(sp => sp.GetRequiredService<IWorkflowBuilder>)
|
||||
.AddAutoMapperProfile<WorkflowDefinitionProfile>(ServiceLifetime.Singleton)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Services;
|
||||
using Elsa.Services.Models;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Elsa.Results
|
||||
{
|
||||
|
|
@ -11,14 +16,25 @@ namespace Elsa.Results
|
|||
public FaultWorkflowResult(Exception exception) : this(exception.Message)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public FaultWorkflowResult(string errorMessage)
|
||||
{
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
protected override void Execute(IWorkflowInvoker invoker, WorkflowExecutionContext workflowContext)
|
||||
|
||||
public override async Task ExecuteAsync(
|
||||
IWorkflowInvoker invoker,
|
||||
WorkflowExecutionContext workflowContext,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var eventHandlers = workflowContext.ServiceProvider.GetServices<IWorkflowEventHandler>();
|
||||
var logger = workflowContext.ServiceProvider.GetRequiredService<ILogger<FaultWorkflowResult>>();
|
||||
var currentActivity = workflowContext.CurrentActivity;
|
||||
|
||||
await eventHandlers.InvokeAsync(
|
||||
x => x.ActivityFaultedAsync(workflowContext, currentActivity, errorMessage, cancellationToken),
|
||||
logger);
|
||||
|
||||
workflowContext.Fault(workflowContext.CurrentActivity, errorMessage);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
using Elsa.Models;
|
||||
using Elsa.Services;
|
||||
using Elsa.Services.Models;
|
||||
using NodaTime;
|
||||
|
||||
namespace Elsa.WorkflowEventHandlers
|
||||
{
|
||||
public class ActivityLoggingWorkflowEventHandler : WorkflowEventHandlerBase
|
||||
{
|
||||
private readonly IClock clock;
|
||||
|
||||
public ActivityLoggingWorkflowEventHandler(IClock clock)
|
||||
{
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
protected override void ActivityExecuted(WorkflowExecutionContext workflowExecutionContext, IActivity activity)
|
||||
{
|
||||
workflowExecutionContext.Workflow.ExecutionLog.Add(
|
||||
new LogEntry(activity.Id, clock.GetCurrentInstant(), "Executed"));
|
||||
}
|
||||
|
||||
protected override void ActivityFaulted(
|
||||
WorkflowExecutionContext workflowExecutionContext,
|
||||
IActivity activity,
|
||||
string message)
|
||||
{
|
||||
workflowExecutionContext.Workflow.ExecutionLog.Add(
|
||||
new LogEntry(activity.Id, clock.GetCurrentInstant(), message, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +1,36 @@
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Persistence;
|
||||
using Elsa.Services.Models;
|
||||
|
||||
namespace Elsa.Services
|
||||
{
|
||||
public class PersistenceWorkflowEventHandler : WorkflowEventHandlerBase
|
||||
{
|
||||
private readonly IWorkflowInstanceStore workflowInstanceStore;
|
||||
|
||||
public PersistenceWorkflowEventHandler(IWorkflowInstanceStore workflowInstanceStore)
|
||||
{
|
||||
this.workflowInstanceStore = workflowInstanceStore;
|
||||
}
|
||||
|
||||
public override async Task InvokingHaltedActivitiesAsync(WorkflowExecutionContext workflowExecutionContext,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await SaveWorkflowAsync(workflowExecutionContext.Workflow, cancellationToken);
|
||||
}
|
||||
|
||||
public override async Task WorkflowInvokedAsync(WorkflowExecutionContext workflowExecutionContext,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await SaveWorkflowAsync(workflowExecutionContext.Workflow, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task SaveWorkflowAsync(Workflow workflow, CancellationToken cancellationToken)
|
||||
{
|
||||
var workflowInstance = workflow.ToInstance();
|
||||
await workflowInstanceStore.SaveAsync(workflowInstance, cancellationToken);
|
||||
}
|
||||
}
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Persistence;
|
||||
using Elsa.Services;
|
||||
using Elsa.Services.Models;
|
||||
|
||||
namespace Elsa.WorkflowEventHandlers
|
||||
{
|
||||
public class PersistenceWorkflowEventHandler : WorkflowEventHandlerBase
|
||||
{
|
||||
private readonly IWorkflowInstanceStore workflowInstanceStore;
|
||||
|
||||
public PersistenceWorkflowEventHandler(IWorkflowInstanceStore workflowInstanceStore)
|
||||
{
|
||||
this.workflowInstanceStore = workflowInstanceStore;
|
||||
}
|
||||
|
||||
public override async Task InvokingHaltedActivitiesAsync(WorkflowExecutionContext workflowExecutionContext,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await SaveWorkflowAsync(workflowExecutionContext.Workflow, cancellationToken);
|
||||
}
|
||||
|
||||
public override async Task WorkflowInvokedAsync(WorkflowExecutionContext workflowExecutionContext,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await SaveWorkflowAsync(workflowExecutionContext.Workflow, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task SaveWorkflowAsync(Workflow workflow, CancellationToken cancellationToken)
|
||||
{
|
||||
var workflowInstance = workflow.ToInstance();
|
||||
await workflowInstanceStore.SaveAsync(workflowInstance, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ namespace Elsa.Dashboard.Areas.Elsa.Controllers
|
|||
private readonly IWorkflowInstanceStore workflowInstanceStore;
|
||||
private readonly IWorkflowPublisher publisher;
|
||||
private readonly IWorkflowSerializer serializer;
|
||||
private readonly IWorkflowFactory workflowFactory;
|
||||
private readonly IOptions<ElsaDashboardOptions> options;
|
||||
private readonly IIdGenerator idGenerator;
|
||||
private readonly INotifier notifier;
|
||||
|
|
@ -36,6 +37,7 @@ namespace Elsa.Dashboard.Areas.Elsa.Controllers
|
|||
IWorkflowInstanceStore workflowInstanceStore,
|
||||
IWorkflowPublisher publisher,
|
||||
IWorkflowSerializer serializer,
|
||||
IWorkflowFactory workflowFactory,
|
||||
IOptions<ElsaDashboardOptions> options,
|
||||
IIdGenerator idGenerator,
|
||||
INotifier notifier)
|
||||
|
|
@ -44,6 +46,7 @@ namespace Elsa.Dashboard.Areas.Elsa.Controllers
|
|||
this.workflowDefinitionStore = workflowDefinitionStore;
|
||||
this.workflowInstanceStore = workflowInstanceStore;
|
||||
this.serializer = serializer;
|
||||
this.workflowFactory = workflowFactory;
|
||||
this.options = options;
|
||||
this.idGenerator = idGenerator;
|
||||
this.notifier = notifier;
|
||||
|
|
@ -72,16 +75,17 @@ namespace Elsa.Dashboard.Areas.Elsa.Controllers
|
|||
[HttpGet("create")]
|
||||
public ViewResult Create()
|
||||
{
|
||||
var workflow = publisher.New();
|
||||
var workflowDefinition = publisher.New();
|
||||
var workflow = workflowFactory.CreateWorkflow(workflowDefinition);
|
||||
|
||||
var model = new WorkflowDefinitionEditModel
|
||||
{
|
||||
Name = workflow.Name,
|
||||
Json = serializer.Serialize(workflow, JsonTokenFormatter.FormatName),
|
||||
Name = workflowDefinition.Name,
|
||||
Workflow = workflow,
|
||||
ActivityDefinitions = options.Value.ActivityDefinitions.ToArray(),
|
||||
IsSingleton = workflow.IsSingleton,
|
||||
IsDisabled = workflow.IsDisabled,
|
||||
Description = workflow.Description
|
||||
IsSingleton = workflowDefinition.IsSingleton,
|
||||
IsDisabled = workflowDefinition.IsDisabled,
|
||||
Description = workflowDefinition.Description
|
||||
};
|
||||
|
||||
return View(model);
|
||||
|
|
@ -103,19 +107,18 @@ namespace Elsa.Dashboard.Areas.Elsa.Controllers
|
|||
[HttpGet("edit/{id}")]
|
||||
public async Task<IActionResult> Edit(string id, CancellationToken cancellationToken)
|
||||
{
|
||||
var workflow = await publisher.GetDraftAsync(id, cancellationToken);
|
||||
var workflowDefinition = await publisher.GetDraftAsync(id, cancellationToken);
|
||||
|
||||
if (workflow == null)
|
||||
if (workflowDefinition == null)
|
||||
return NotFound();
|
||||
|
||||
var model = new WorkflowDefinitionEditModel
|
||||
{
|
||||
Id = workflow.DefinitionId,
|
||||
Name = workflow.Name,
|
||||
Description = workflow.Description,
|
||||
IsSingleton = workflow.IsSingleton,
|
||||
IsDisabled = workflow.IsDisabled,
|
||||
Json = serializer.Serialize(workflow, JsonTokenFormatter.FormatName),
|
||||
Id = workflowDefinition.DefinitionId,
|
||||
Name = workflowDefinition.Name,
|
||||
Description = workflowDefinition.Description,
|
||||
IsSingleton = workflowDefinition.IsSingleton,
|
||||
IsDisabled = workflowDefinition.IsDisabled,
|
||||
ActivityDefinitions = options.Value.ActivityDefinitions.ToArray()
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using Elsa.Models;
|
|||
using Elsa.Persistence;
|
||||
using Elsa.Serialization;
|
||||
using Elsa.Serialization.Formatters;
|
||||
using Elsa.Services;
|
||||
using Jint.Native.Json;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
|
@ -25,6 +26,7 @@ namespace Elsa.Dashboard.Areas.Elsa.Controllers
|
|||
private readonly IWorkflowDefinitionStore workflowDefinitionStore;
|
||||
private readonly IOptions<ElsaDashboardOptions> options;
|
||||
private readonly IWorkflowSerializer serializer;
|
||||
private readonly IWorkflowFactory workflowFactory;
|
||||
private readonly INotifier notifier;
|
||||
|
||||
public WorkflowInstanceController(
|
||||
|
|
@ -32,12 +34,14 @@ namespace Elsa.Dashboard.Areas.Elsa.Controllers
|
|||
IWorkflowDefinitionStore workflowDefinitionStore,
|
||||
IOptions<ElsaDashboardOptions> options,
|
||||
IWorkflowSerializer serializer,
|
||||
IWorkflowFactory workflowFactory,
|
||||
INotifier notifier)
|
||||
{
|
||||
this.workflowInstanceStore = workflowInstanceStore;
|
||||
this.workflowDefinitionStore = workflowDefinitionStore;
|
||||
this.options = options;
|
||||
this.serializer = serializer;
|
||||
this.workflowFactory = workflowFactory;
|
||||
this.notifier = notifier;
|
||||
}
|
||||
|
||||
|
|
@ -90,14 +94,13 @@ namespace Elsa.Dashboard.Areas.Elsa.Controllers
|
|||
cancellationToken
|
||||
);
|
||||
|
||||
var json = serializer.Serialize(definition, JsonTokenFormatter.FormatName);
|
||||
var workflow = workflowFactory.CreateWorkflow(definition, Variables.Empty, instance);
|
||||
|
||||
var model = new WorkflowInstanceDetailsModel
|
||||
{
|
||||
ReturnUrl = returnUrl,
|
||||
Json = json,
|
||||
WorkflowDefinition = definition,
|
||||
WorkflowInstance = instance,
|
||||
Workflow = workflow,
|
||||
ActivityDefinitions = options.Value.ActivityDefinitions.ToArray()
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using Elsa.Services.Models;
|
||||
using Elsa.WorkflowDesigner.Models;
|
||||
|
||||
namespace Elsa.Dashboard.Areas.Elsa.ViewModels
|
||||
|
|
@ -5,6 +6,7 @@ namespace Elsa.Dashboard.Areas.Elsa.ViewModels
|
|||
public class WorkflowDefinitionEditModel
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public Workflow Workflow { get; set; }
|
||||
public string Json { get; set; }
|
||||
public string SubmitAction { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Elsa.Models;
|
||||
using Elsa.Services.Models;
|
||||
using ActivityDefinition = Elsa.WorkflowDesigner.Models.ActivityDefinition;
|
||||
|
||||
namespace Elsa.Dashboard.Areas.Elsa.ViewModels
|
||||
|
|
@ -6,9 +7,8 @@ namespace Elsa.Dashboard.Areas.Elsa.ViewModels
|
|||
public class WorkflowInstanceDetailsModel
|
||||
{
|
||||
public string ReturnUrl { get; set; }
|
||||
public string Json { get; set; }
|
||||
public WorkflowDefinitionVersion WorkflowDefinition { get; set; }
|
||||
public WorkflowInstance WorkflowInstance { get; set; }
|
||||
public Workflow Workflow { get; set; }
|
||||
public ActivityDefinition[] ActivityDefinitions { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -24,8 +24,7 @@
|
|||
</div>
|
||||
<div class="card-body">
|
||||
<form asp-action="@formAction">
|
||||
<input asp-for="Json" id="workflowData" type="hidden"/>
|
||||
<vc:workflow-designer id="designerHost" activity-definitions="@Model.ActivityDefinitions"/>
|
||||
<vc:workflow-designer id="designerHost" data-activity-definitions="@Model.ActivityDefinitions" data-workflow="@Model.Workflow"/>
|
||||
<div class="text-center">
|
||||
<button asp-for="SubmitAction" name="SubmitAction" type="submit" value="draft" class="btn btn-primary mt-4">Save Draft</button>
|
||||
<button asp-for="SubmitAction" name="SubmitAction" type="submit" value="publish" class="btn btn-success mt-4">Publish</button>
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
<!-- CSS Files -->
|
||||
<link href="~/assets/css/argon-dashboard.css?v=1.1.0" rel="stylesheet"/>
|
||||
<link href="~/assets/css/elsa.css?v=1.1.0" rel="stylesheet"/>
|
||||
<script src='https://unpkg.com/@@elsa-workflows/elsa-workflow-designer@0.0.49/dist/elsa-workflow-designer.js'></script>
|
||||
<script src='https://unpkg.com/@@elsa-workflows/elsa-workflow-designer@0.0.51/dist/elsa-workflow-designer.js'></script>
|
||||
@await RenderSectionAsync("HeadScripts", false)
|
||||
</head>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
@model WorkflowInstanceDetailsModel
|
||||
<div class="container-fluid mt--7" data-workflow="@Model.Json">
|
||||
<div class="container-fluid mt-7">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="card bg-secondary shadow">
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<vc:workflow-designer id="designerHost" activity-definitions="@Model.ActivityDefinitions" />
|
||||
<vc:workflow-designer id="designerHost" activity-definitions="@Model.ActivityDefinitions" workflow="@Model.Workflow"/>
|
||||
<div class="text-center">
|
||||
<a href="@Model.ReturnUrl" class="btn btn-primary mt-4">Back</a>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
const designer = document.querySelector("#designerHost");
|
||||
let workflow = null;
|
||||
|
||||
//designer.addEventListener('componentReady', onWorkflowDesignerReady);
|
||||
|
||||
function exportWorkflow() {
|
||||
designer.export({
|
||||
|
|
@ -10,17 +7,4 @@ function exportWorkflow() {
|
|||
mimeType: 'application/json',
|
||||
displayName: 'JSON'
|
||||
});
|
||||
}
|
||||
|
||||
function onWorkflowDesignerReady() {
|
||||
const input = document.querySelector('[data-workflow]');
|
||||
const json = input.attributes['data-workflow'].value;
|
||||
|
||||
if (!json)
|
||||
return;
|
||||
|
||||
designer.workflow = workflow = JSON.parse(json);
|
||||
}
|
||||
|
||||
// Temporary workaround until I figure out how to listen for the workflow designer component's ready event.
|
||||
setTimeout(onWorkflowDesignerReady, 100);
|
||||
}
|
||||
|
|
@ -63,13 +63,13 @@ namespace Elsa.WorkflowDesigner
|
|||
yield break;
|
||||
|
||||
yield return new ActivityPropertyDescriptor
|
||||
{
|
||||
Name = (activityProperty.Name ?? propertyInfo.Name).Camelize(),
|
||||
Label = activityProperty.Label ?? propertyInfo.Name.Humanize(LetterCasing.Title),
|
||||
Type = (activityProperty.Type ?? DeterminePropertyType(propertyInfo)).Camelize(),
|
||||
Hint = activityProperty.Hint,
|
||||
Options = GetPropertyTypeOptions(propertyInfo)
|
||||
};
|
||||
(
|
||||
(activityProperty.Name ?? propertyInfo.Name).Camelize(),
|
||||
(activityProperty.Type ?? DeterminePropertyType(propertyInfo)).Camelize(),
|
||||
activityProperty.Label ?? propertyInfo.Name.Humanize(LetterCasing.Title),
|
||||
activityProperty.Hint,
|
||||
GetPropertyTypeOptions(propertyInfo)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<PackageVersion>1.0.0</PackageVersion>
|
||||
<Authors>Sipke Schoorstra</Authors>
|
||||
<Description>Elsa is a set of workflowing libraries and tools to enable super-fast workflowing capabilities in any .NET Core application.</Description>
|
||||
|
|
|
|||
|
|
@ -4,14 +4,19 @@ namespace Elsa.WorkflowDesigner.Models
|
|||
{
|
||||
public ActivityDefinition()
|
||||
{
|
||||
Type = "Activity";
|
||||
Properties = new ActivityPropertyDescriptor[0];
|
||||
Category = "Miscellaneous";
|
||||
DisplayName = "Activity";
|
||||
Properties = new ActivityPropertyDescriptor[0];
|
||||
Designer = new ActivityDesignerSettings();
|
||||
}
|
||||
|
||||
public string Type { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string Category { get; set; }
|
||||
public string Icon { get; set; }
|
||||
public string? Icon { get; set; }
|
||||
public ActivityPropertyDescriptor[] Properties { get; set; }
|
||||
public ActivityDesignerSettings Designer { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ namespace Elsa.WorkflowDesigner.Models
|
|||
{
|
||||
public class ActivityDesignerSettings
|
||||
{
|
||||
public object Description { get; set; }
|
||||
public object Outcomes { get; set; }
|
||||
public object? Description { get; set; }
|
||||
public object? Outcomes { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -2,10 +2,19 @@ namespace Elsa.WorkflowDesigner.Models
|
|||
{
|
||||
public class ActivityPropertyDescriptor
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string Label { get; set; }
|
||||
public string Hint { get; set; }
|
||||
public object Options { get; set; }
|
||||
public ActivityPropertyDescriptor(string name, string type, string label, string? hint = null, object? options = null)
|
||||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
Label = label;
|
||||
Hint = hint;
|
||||
Options = options;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
public string Type { get; }
|
||||
public string Label { get; }
|
||||
public string? Hint { get; }
|
||||
public object? Options { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +1,47 @@
|
|||
using Elsa.WorkflowDesigner.Models;
|
||||
using Elsa.Serialization;
|
||||
using Elsa.Services.Models;
|
||||
using Elsa.WorkflowDesigner.ViewModels;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using ActivityDefinition = Elsa.WorkflowDesigner.Models.ActivityDefinition;
|
||||
|
||||
namespace Elsa.WorkflowDesigner.ViewComponents
|
||||
{
|
||||
public class WorkflowDesignerViewComponent : ViewComponent
|
||||
{
|
||||
public IViewComponentResult Invoke(ActivityDefinition[] activityDefinitions, string id)
|
||||
private readonly IWorkflowSerializer serializer;
|
||||
|
||||
public WorkflowDesignerViewComponent(IWorkflowSerializer serializer)
|
||||
{
|
||||
var model = new WorkflowDesignerViewComponentModel
|
||||
{
|
||||
Id = id,
|
||||
ActivityDefinitionsJson = GetActivityDefinitionOptions(activityDefinitions)
|
||||
};
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
public IViewComponentResult Invoke(
|
||||
string id,
|
||||
ActivityDefinition[]? activityDefinitions = null,
|
||||
Workflow? workflow = null)
|
||||
{
|
||||
var model = new WorkflowDesignerViewComponentModel(
|
||||
id,
|
||||
Serialize(activityDefinitions ?? new ActivityDefinition[0]),
|
||||
Serialize(workflow)
|
||||
);
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
private string GetActivityDefinitionOptions(ActivityDefinition[] activityDefinitions)
|
||||
private static string? Serialize(object? value)
|
||||
{
|
||||
var definitions = activityDefinitions ?? new ActivityDefinition[0];
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
||||
NullValueHandling = NullValueHandling.Ignore
|
||||
};
|
||||
return JsonConvert.SerializeObject(definitions, settings);
|
||||
return JsonConvert.SerializeObject(value, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,15 @@ namespace Elsa.WorkflowDesigner.ViewModels
|
|||
{
|
||||
public class WorkflowDesignerViewComponentModel
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string ActivityDefinitionsJson { get; set; }
|
||||
public WorkflowDesignerViewComponentModel(string id, string? activityDefinitionsJson, string? workflowJson)
|
||||
{
|
||||
Id = id;
|
||||
ActivityDefinitionsJson = activityDefinitionsJson;
|
||||
WorkflowJson = workflowJson;
|
||||
}
|
||||
|
||||
public string Id { get; }
|
||||
public string? ActivityDefinitionsJson { get; set; }
|
||||
public string? WorkflowJson { get; set; }
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue