Implement activity execution logging (#89)

* Implement activity logging
* Update Calculator example
This commit is contained in:
Sipke Schoorstra 2019-10-14 19:46:55 +02:00 committed by GitHub
parent bdbede4e1b
commit 012e12c51c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 263 additions and 138 deletions

View file

@ -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)

View file

@ -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();
}
}

View file

@ -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>

View file

@ -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)
{
}

View file

@ -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;
}
}

View file

@ -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)

View file

@ -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);
}
}

View file

@ -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));
}
}
}

View file

@ -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);
}
}
}

View file

@ -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()
};

View file

@ -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()
};

View file

@ -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; }

View file

@ -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; }
}
}

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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);
}

View file

@ -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)
);
}
}

View file

@ -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>

View file

@ -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; }
}

View file

@ -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; }
}
}

View file

@ -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; }
}
}

View file

@ -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);
}
}
}

View file

@ -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; }
}
}