Implemented logging

This commit is contained in:
Sipke Schoorstra 2019-01-11 22:46:01 +01:00
parent 3766f72d4e
commit b946bc32a6
6 changed files with 116 additions and 35 deletions

View file

@ -13,8 +13,8 @@ namespace Elsa {
});
}
static getSourceEndpointOptions(activityId: any, endpointName: any, hasExecuted: boolean, hasFaulted: boolean): any {
const fill = hasFaulted ? '#d23c3c' : hasExecuted? '#6faa44' : '#7da7f2';
static getSourceEndpointOptions(activityId: any, endpointName: any, hasExecuted: boolean, hasFaulted: boolean, isBlocking: boolean): any {
const fill = isBlocking ? '#7da7f2' : hasFaulted ? '#d23c3c' : hasExecuted? '#6faa44' : '#7da7f2';
const stroke = fill;
return {
endpoint: 'Dot',
@ -29,19 +29,19 @@ namespace Elsa {
connector: ['Flowchart', {stub: [40, 60], gap: 0, cornerRadius: 5, alwaysRespectStubs: true}],
connectorStyle: {
strokeWidth: 2,
stroke: '#999999',
stroke: !isBlocking && (hasFaulted || hasExecuted) ? fill : '#999999',
joinstyle: 'round',
outlineStroke: 'white',
outlineWidth: 2
outlineWidth: 1
},
hoverPaintStyle: {
fill: stroke,
stroke: fill
},
connectorHoverStyle: {
connectorHoverStyle: isBlocking || hasFaulted || hasExecuted ? null : {
strokeWidth: 3,
stroke: stroke,
outlineWidth: 5,
outlineWidth: 1,
outlineStroke: 'white'
},
connectorOverlays: [['Label', {location: [3, -1.5], cssClass: 'endpointSourceLabel'}]],

View file

@ -13,7 +13,6 @@ namespace Elsa {
private readonly container: JQuery<HTMLElement>;
private readonly canvasContainer: JQuery<HTMLElement>;
private readonly isDefinition: boolean;
private readonly executionLog: Array<LogEntry>;
private readonly plumber: any;
private dragStart: { left: number; top: number };
private hasDragged: boolean;
@ -22,7 +21,6 @@ namespace Elsa {
this.container = $(containerElement);
this.canvasContainer = this.container.find('.workflow-canvas');
this.isDefinition = this.canvasContainer.data('workflow-is-definition');
this.executionLog = this.canvasContainer.data('workflow-execution-log');
this.plumber = WorkflowDesignerConfig.createJsPlumbInstance(this.canvasContainer[0]);
this.initializeNodes();
this.container.on('contextmenu', '.workflow-canvas', this.onCanvasContextMenu);
@ -31,6 +29,10 @@ namespace Elsa {
this.canvasContainer.on('click', '.activity .context-menu .dropdown-item', this.onActivityContextMenuItemClick);
this.canvasContainer.on('click', '.canvas-context-menu .dropdown-item', this.onCanvasContextMenuItemClick);
$(document).on('click', 'body', this.hideContextMenu);
this.canvasContainer.find('[data-toggle="popover"]').popover({
container: '.workflow-canvas'
})
}
public getWorkflow = (): IWorkflow => {
@ -247,7 +249,8 @@ namespace Elsa {
for (let endpoint of endpoints) {
const hasExecuted: boolean = $activityElement.data('activity-executed');
const hasFaulted: boolean = $activityElement.data('activity-faulted');
const sourceEndpointOptions: any = WorkflowDesignerConfig.getSourceEndpointOptions(activityId, endpoint.name, hasExecuted, hasFaulted);
const isBlocking: boolean = $activityElement.data('activity-blocking');
const sourceEndpointOptions: any = WorkflowDesignerConfig.getSourceEndpointOptions(activityId, endpoint.name, hasExecuted, hasFaulted, isBlocking);
this.plumber.addEndpoint(activityElement, {
connectorOverlays: [['Label', {
label: endpoint.name,

View file

@ -6,7 +6,7 @@
.activity {
display: none;
border: 1px solid #5a8fee;
border: 1px solid #cccccc;
box-shadow: 2px 2px 19px #aaa;
-o-box-shadow: 2px 2px 9px #aaa;
-webkit-box-shadow: 2px 2px 9px #aaa;
@ -30,18 +30,49 @@
-o-transition: -o-box-shadow 0.15s ease-in;
transition: box-shadow 0.15s ease-in;
&.activity-executed {
border: 1px solid #6faa44;
.activity-status {
position: absolute;
right: 4px;
bottom: 4px;
cursor: pointer;
i {
font-size: 2em;
}
}
&.activity-start, &.activity-blocking {
background-color: #f1f1f1;
color: #3d3d3d;
&.activity-definition {
border: 1px solid #5a8fee;
}
&.activity-executed {
border: 1px solid #6faa44;
.activity-status {
i {
color: #6faa44;
}
}
}
&.activity-blocking {
border: 1px solid #5a8fee;
.activity-status {
i {
color: #5a8fee;
}
}
}
&.activity-faulted {
border: 1px solid #d23c3c;
.activity-status {
i {
color: #d23c3c;
}
}
}
header {
@ -53,6 +84,9 @@
}
}
}
}
.jtk-connector {

View file

@ -30,6 +30,10 @@ namespace Elsa.Web.Components.Services
shape.ActivityDescriptor = descriptor;
shape.Activity = activity;
shape.Designer = designerMetadata;
shape.IsBlocking = false;
shape.HasExecuted = false;
shape.HasFaulted = false;
shape.WorkflowIsDefinition = true;
return shape;
}

View file

@ -2,6 +2,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using Elsa.Extensions;
using Elsa.Models;
using Elsa.Persistence;
using Elsa.Web.Components.ViewModels;
@ -35,9 +37,14 @@ namespace Elsa.Web.Components.ViewComponents
private async Task<dynamic> BuildActivityShapeAsync(Workflow workflow, IActivity activity, CancellationToken cancellationToken)
{
var shape = (dynamic)await activityShapeFactory.BuildDesignShapeAsync(activity, cancellationToken);
var logEntries = workflow.ExecutionLog.Where(x => x.ActivityId == activity.Id).OrderByDescending(x => x.Timestamp).ToList();
var logEntries = workflow.ExecutionLog.Where(x => x.ActivityId == activity.Id).OrderBy(x => x.Timestamp).ToList();
shape.LogEntries = logEntries;
shape.BlockingActivities = workflow.BlockingActivities;
shape.HasExecuted = logEntries.Any();
shape.HasFaulted = logEntries.Any(x => x.Faulted);
shape.IsBlocking = workflow.BlockingActivities.Any(x => x.Id == activity.Id);
shape.WorkflowIsDefinition = workflow.IsDefinition();
return shape;
}

View file

@ -1,6 +1,7 @@
@using Elsa
@using Elsa.Models
@using Elsa.Web.Components.Models
@using Microsoft.AspNetCore.Html
@using Microsoft.Extensions.Localization
@using Newtonsoft.Json
@using Newtonsoft.Json.Serialization
@ -14,28 +15,41 @@
var activityJson = JsonConvert.SerializeObject(activity, serializerSettings);
var activityEndpoints = JsonConvert.SerializeObject(endpoints, serializerSettings);
var designer = (ActivityDesignerMetadata) Model.Designer;
var logEntries = Model.LogEntries != null ? (ICollection<LogEntry>)Model.LogEntries : new List<LogEntry>();
var logEntriesJson = JsonConvert.SerializeObject(logEntries, serializerSettings);
var hasFaulted = logEntries.Any(x => x.Faulted);
var hasExecuted = logEntries.Any();
var hasFaulted = (bool)Model.HasFaulted;
var hasExecuted = (bool)Model.HasExecuted;
var isBlocking = (bool)Model.IsBlocking;
var isDefinition = (bool)Model.WorkflowIsDefinition;
var isInstance = !isDefinition;
var cssClasses = new List<string> { "activity" };
var left = designer.X;
var top = designer.Y;
if (hasExecuted)
if (isInstance)
{
cssClasses.Add("activity-executed");
if (hasExecuted)
{
cssClasses.Add("activity-executed");
}
if (hasFaulted)
{
cssClasses.Add("activity-faulted");
}
if (isBlocking)
{
cssClasses.Add("activity-blocking");
}
}
else
{
cssClasses.Add("activity-definition");
}
if (hasFaulted)
{
cssClasses.Add("activity-faulted");
}
var cssClass = string.Join(" ", cssClasses);
var activityClass = string.Join(" ", cssClasses);
}
<div
class="@cssClass"
class="@activityClass"
style="left: @(left)px; top: @(top)px;"
id="@($"activity-{id}")"
title="@descriptor.DisplayText"
@ -44,11 +58,30 @@
data-activity-model="@activityJson"
data-activity-endpoints="@activityEndpoints"
data-activity-executed="@hasExecuted.ToString().ToLower()"
data-activity-faulted="@hasFaulted.ToString().ToLower()"
data-activity-log="@logEntriesJson">
data-activity-blocking="@isBlocking.ToString().ToLower()"
data-activity-faulted="@hasFaulted.ToString().ToLower()">
@await DisplayAsync(Model.Content)
<div class="dropdown-menu context-menu activity-context-menu">
<a class="dropdown-item" href="#edit">Edit</a>
<a class="dropdown-item" href="#delete">Delete</a>
</div>
@if (isDefinition)
{
<div class="dropdown-menu context-menu activity-context-menu">
<a class="dropdown-item" href="#edit">Edit</a>
<a class="dropdown-item" href="#delete">Delete</a>
</div>
}
@if (isInstance)
{
var title = hasFaulted ? T["Faulted"] : isBlocking ? T["Awaiting Input"] : T["Executed"];
var statusClass = hasFaulted ? "fas fa-exclamation-circle" : isBlocking ? "fas fa-hourglass-half" : "fas fa-check-circle";
var logEntries = Model.LogEntries != null ? (ICollection<LogEntry>) Model.LogEntries : new List<LogEntry>();
var logEntriesText = new HtmlString(string.Join("<br/>", logEntries.Select(x => $"{x.Timestamp}: {x.Message}")));
<div class="activity-status rounded-circle" tabindex="0" role="button" data-toggle="popover" data-trigger="focus" title="@title" data-content="@logEntriesText" data-html="true">
@if (hasFaulted || isBlocking || hasExecuted)
{
<i class="@statusClass"></i>
}
</div>
}
</div>