Incremental work on workflow journal UI
This commit is contained in:
parent
c1c5673726
commit
e724d6030a
|
|
@ -2,8 +2,21 @@
|
|||
|
||||
namespace Elsa.Models
|
||||
{
|
||||
public record SimpleException(Type Type, string Message, string StackTrace, SimpleException? InnerException = default)
|
||||
public class SimpleException
|
||||
{
|
||||
public SimpleException(Type type, string message, string stackTrace, SimpleException? innerException = default)
|
||||
{
|
||||
Type = type;
|
||||
Message = message;
|
||||
StackTrace = stackTrace;
|
||||
InnerException = innerException;
|
||||
}
|
||||
|
||||
public Type Type { get; set; }
|
||||
public string Message { get; set; }
|
||||
public string StackTrace { get; set; }
|
||||
public SimpleException? InnerException { get; set; }
|
||||
|
||||
public static SimpleException? FromException(Exception? ex)
|
||||
{
|
||||
if (ex == null)
|
||||
|
|
@ -12,7 +25,7 @@ namespace Elsa.Models
|
|||
var simpleException = new SimpleException(ex.GetType(), ex.Message, ex.StackTrace);
|
||||
|
||||
if (ex.InnerException != null)
|
||||
simpleException = simpleException with { InnerException = FromException(ex.InnerException) };
|
||||
simpleException.InnerException = FromException(ex.InnerException);
|
||||
|
||||
return simpleException;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -212,6 +212,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
services
|
||||
.AddAutoMapperProfile<NodaTimeProfile>()
|
||||
.AddAutoMapperProfile<CloningProfile>()
|
||||
.AddAutoMapperProfile<ExceptionProfile>()
|
||||
.AddTransient<ExceptionConverter>()
|
||||
.AddSingleton<ICloner, AutoMapperCloner>();
|
||||
|
||||
// Caching.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
using Elsa.Events;
|
||||
using Elsa.Models;
|
||||
using Elsa.Services;
|
||||
using Elsa.Services.Models;
|
||||
using MediatR;
|
||||
|
|
@ -10,8 +12,14 @@ namespace Elsa.Handlers
|
|||
public class WriteWorkflowExecutionLog : INotificationHandler<ActivityExecuting>, INotificationHandler<ActivityExecutionResultExecuted>, INotificationHandler<ActivityFaulted>
|
||||
{
|
||||
private readonly IWorkflowExecutionLog _workflowExecutionLog;
|
||||
public WriteWorkflowExecutionLog(IWorkflowExecutionLog workflowExecutionLog) => _workflowExecutionLog = workflowExecutionLog;
|
||||
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public WriteWorkflowExecutionLog(IWorkflowExecutionLog workflowExecutionLog, IMapper mapper)
|
||||
{
|
||||
_workflowExecutionLog = workflowExecutionLog;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public async Task Handle(ActivityExecuting notification, CancellationToken cancellationToken)
|
||||
{
|
||||
var activityExecutionContext = notification.ActivityExecutionContext;
|
||||
|
|
@ -43,11 +51,11 @@ namespace Elsa.Handlers
|
|||
public async Task Handle(ActivityFaulted notification, CancellationToken cancellationToken)
|
||||
{
|
||||
var exception = notification.Exception;
|
||||
var exceptionModel = _mapper.Map<SimpleException>(exception);
|
||||
|
||||
var data = new
|
||||
{
|
||||
exception.Message,
|
||||
exception.StackTrace
|
||||
exception = exceptionModel
|
||||
};
|
||||
|
||||
await WriteEntryAsync("Faulted", exception.Message, notification.ActivityExecutionContext, data, cancellationToken);
|
||||
|
|
|
|||
28
src/core/Elsa.Core/Mapping/ExceptionConverter.cs
Normal file
28
src/core/Elsa.Core/Mapping/ExceptionConverter.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using AutoMapper;
|
||||
using Elsa.Models;
|
||||
|
||||
namespace Elsa.Mapping
|
||||
{
|
||||
public class ExceptionConverter : ITypeConverter<Exception, SimpleException>
|
||||
{
|
||||
public SimpleException Convert(Exception source, SimpleException? destination, ResolutionContext context)
|
||||
{
|
||||
var model = SimpleException.FromException(source)!;
|
||||
|
||||
if (destination != null)
|
||||
{
|
||||
destination.Message = model.Message;
|
||||
destination.Type = model.Type;
|
||||
destination.StackTrace = model.StackTrace;
|
||||
destination.InnerException = model.InnerException;
|
||||
}
|
||||
else
|
||||
{
|
||||
destination = model;
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/core/Elsa.Core/Mapping/ExceptionProfile.cs
Normal file
14
src/core/Elsa.Core/Mapping/ExceptionProfile.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using AutoMapper;
|
||||
using Elsa.Models;
|
||||
|
||||
namespace Elsa.Mapping
|
||||
{
|
||||
public class ExceptionProfile : Profile
|
||||
{
|
||||
public ExceptionProfile()
|
||||
{
|
||||
CreateMap<Exception, SimpleException>().ConvertUsing<ExceptionConverter>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -177,7 +177,8 @@ export class ElsaWorkflowInstanceJournal {
|
|||
<div class="elsa-px-4 sm:elsa-px-6">
|
||||
<div class="elsa-flex elsa-flex-col elsa-items-end">
|
||||
<div class="elsa-ml-3 h-7 elsa-flex elsa-items-center">
|
||||
<button type="button" onClick={e => this.onCloseClick()} class="elsa-bg-white elsa-rounded-md elsa-text-gray-400 hover:elsa-text-gray-500 focus:elsa-outline-none focus:elsa-ring-2 focus:elsa-ring-offset-2 focus:elsa-ring-blue-500">
|
||||
<button type="button" onClick={e => this.onCloseClick()}
|
||||
class="elsa-bg-white elsa-rounded-md elsa-text-gray-400 hover:elsa-text-gray-500 focus:elsa-outline-none focus:elsa-ring-2 focus:elsa-ring-offset-2 focus:elsa-ring-blue-500">
|
||||
<span class="elsa-sr-only">Close panel</span>
|
||||
<svg class="elsa-h-6 elsa-w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
||||
|
|
@ -259,7 +260,7 @@ export class ElsaWorkflowInstanceJournal {
|
|||
if (typeof value == 'string')
|
||||
valueText = value;
|
||||
else if (typeof value == 'object')
|
||||
valueText = JSON.stringify(value);
|
||||
valueText = JSON.stringify(value, null, 1);
|
||||
else if (typeof value == 'undefined')
|
||||
valueText = null;
|
||||
else
|
||||
|
|
@ -274,8 +275,8 @@ export class ElsaWorkflowInstanceJournal {
|
|||
<li>
|
||||
<div onClick={() => this.onRecordClick(record)} class={`${recordClass} elsa-border-2 elsa-cursor-pointer elsa-p-4 elsa-rounded`}>
|
||||
<div class="elsa-relative elsa-pb-10">
|
||||
{isLastItem ? undefined : <div class="elsa-flex elsa-absolute top-8 left-4 -elsa-ml-px elsa-h-full elsa-w-0.5 elsa-bg-gray-200">
|
||||
<div class="elsa-flex elsa-flex-1 elsa-items-center elsa-relative -elsa-left-5">
|
||||
{isLastItem ? undefined : <div class="elsa-flex elsa-absolute top-8 elsa-left-4 -elsa-ml-px elsa-h-full elsa-w-0.5 elsa-bg-gray-200">
|
||||
<div class="elsa-flex elsa-flex-1 elsa-items-center elsa-relative elsa-right-10">
|
||||
<span class="elsa-flex-1 elsa-text-sm elsa-text-gray-500 elsa-w-max elsa-bg-white elsa-p-1 elsa-rounded">{deltaTimeText}</span>
|
||||
</div>
|
||||
</div>}
|
||||
|
|
@ -303,27 +304,31 @@ export class ElsaWorkflowInstanceJournal {
|
|||
</div>
|
||||
</div>
|
||||
<div class="elsa-ml-12 elsa-mt-2">
|
||||
<dl class="elsa-grid elsa-grid-cols-1 elsa-gap-x-4 elsa-gap-y-8 sm:elsa-grid-cols-2">
|
||||
<div class="sm:elsa-col-span-1">
|
||||
<dt class="elsa-text-sm elsa-font-medium elsa-text-gray-500">Activity ID</dt>
|
||||
<dd class="elsa-mt-1 elsa-text-sm elsa-text-gray-900 elsa-mb-2">{record.activityId}</dd>
|
||||
<dl class="sm:elsa-divide-y sm:elsa-divide-gray-200">
|
||||
<div class="elsa-grid elsa-grid-cols-2 elsa-gap-x-4 elsa-gap-y-8 sm:elsa-grid-cols-2">
|
||||
<div class="sm:elsa-col-span-2">
|
||||
<dt class="elsa-text-sm elsa-font-medium elsa-text-gray-500">Activity ID</dt>
|
||||
<dd class="elsa-mt-1 elsa-text-sm elsa-text-gray-900 elsa-mb-2">{record.activityId}</dd>
|
||||
</div>
|
||||
{collection.map(filteredRecordData, (v, k) => (
|
||||
<div class="sm:elsa-col-span-2">
|
||||
<dt class="elsa-text-sm elsa-font-medium elsa-text-gray-500">{k}</dt>
|
||||
<dd class="elsa-mt-1 elsa-text-sm elsa-text-gray-900 elsa-mb-2">
|
||||
<pre>{v}</pre>
|
||||
</dd>
|
||||
</div>
|
||||
))}
|
||||
{record.message ? (
|
||||
<div class="sm:elsa-col-span-2">
|
||||
<dt class="elsa-text-sm elsa-font-medium elsa-text-gray-500">
|
||||
Message
|
||||
</dt>
|
||||
<dd class="elsa-mt-1 elsa-text-sm elsa-text-gray-900">
|
||||
{record.message}
|
||||
</dd>
|
||||
</div>
|
||||
) : undefined}
|
||||
</div>
|
||||
{collection.map(filteredRecordData, (v, k) => (
|
||||
<div class="sm:elsa-col-span-1">
|
||||
<dt class="elsa-text-sm elsa-font-medium elsa-text-gray-500">{k}</dt>
|
||||
<dd class="elsa-mt-1 elsa-text-sm elsa-text-gray-900 elsa-mb-2"><pre>{v}</pre></dd>
|
||||
</div>
|
||||
))}
|
||||
{record.message ? (
|
||||
<div class="sm:elsa-col-span-1">
|
||||
<dt class="elsa-text-sm elsa-font-medium elsa-text-gray-500">
|
||||
Message
|
||||
</dt>
|
||||
<dd class="elsa-mt-1 elsa-text-sm elsa-text-gray-900">
|
||||
{record.message}
|
||||
</dd>
|
||||
</div>
|
||||
) : undefined}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,25 +6,26 @@ using Elsa.Services.Models;
|
|||
|
||||
namespace Elsa.Server.Api.Mapping
|
||||
{
|
||||
public class ActivityBlueprintConverter : ITypeConverter<IActivityBlueprint, ActivityBlueprintModel>
|
||||
public class ActivityBlueprintConverter : ITypeConverter<IActivityBlueprint, ActivityBlueprintModel?>
|
||||
{
|
||||
public const string ActivityPropertiesKey = "ActivityProperties";
|
||||
|
||||
public ActivityBlueprintModel Convert(IActivityBlueprint source, ActivityBlueprintModel destination, ResolutionContext context)
|
||||
public ActivityBlueprintModel Convert(IActivityBlueprint source, ActivityBlueprintModel? destination, ResolutionContext context)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Id = source.Id,
|
||||
Name = source.Name,
|
||||
DisplayName = source.DisplayName,
|
||||
Description = source.Description,
|
||||
Type = source.Type,
|
||||
ParentId = source.Parent?.Id,
|
||||
PersistWorkflow = source.PersistWorkflow,
|
||||
LoadWorkflowContext = source.LoadWorkflowContext,
|
||||
SaveWorkflowContext = source.SaveWorkflowContext,
|
||||
Properties = GetProperties(source, context)
|
||||
};
|
||||
destination ??= new ActivityBlueprintModel();
|
||||
|
||||
destination.Id = source.Id;
|
||||
destination. Name = source.Name;
|
||||
destination.DisplayName = source.DisplayName;
|
||||
destination.Description = source.Description;
|
||||
destination.Type = source.Type;
|
||||
destination.ParentId = source.Parent?.Id;
|
||||
destination.PersistWorkflow = source.PersistWorkflow;
|
||||
destination.LoadWorkflowContext = source.LoadWorkflowContext;
|
||||
destination.SaveWorkflowContext = source.SaveWorkflowContext;
|
||||
destination.Properties = GetProperties(source, context);
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
private Variables GetProperties(IActivityBlueprint activityBlueprint, ResolutionContext context)
|
||||
|
|
|
|||
|
|
@ -4,14 +4,17 @@ using Elsa.Services.Models;
|
|||
|
||||
namespace Elsa.Server.Api.Mapping
|
||||
{
|
||||
public class ConnectionConverter : ITypeConverter<IConnection, ConnectionModel>
|
||||
public class ConnectionConverter : ITypeConverter<IConnection, ConnectionModel?>
|
||||
{
|
||||
public ConnectionModel Convert(IConnection source, ConnectionModel destination, ResolutionContext context) =>
|
||||
new()
|
||||
{
|
||||
SourceActivityId = source.Source.Activity.Id,
|
||||
TargetActivityId = source.Target.Activity.Id,
|
||||
Outcome = source.Source.Outcome
|
||||
};
|
||||
public ConnectionModel Convert(IConnection source, ConnectionModel? destination, ResolutionContext context)
|
||||
{
|
||||
destination ??= new ConnectionModel();
|
||||
|
||||
destination.SourceActivityId = source.Source.Activity.Id;
|
||||
destination.TargetActivityId = source.Target.Activity.Id;
|
||||
destination.Outcome = source.Source.Outcome;
|
||||
|
||||
return destination;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue