Fix metadata and serialization

This commit is contained in:
Sipke Schoorstra 2022-03-24 12:56:31 +01:00
parent c74b105e19
commit c13825a930
17 changed files with 52 additions and 21 deletions

View file

@ -51,7 +51,7 @@ public static partial class Workflows
draft.Root = root;
draft.Metadata = draft.Metadata with
draft.WorkflowMetadata = draft.WorkflowMetadata with
{
Name = model.Name,
Description = model.Description

View file

@ -28,5 +28,6 @@ public class ActivityAttribute : Attribute
public string? Namespace { get; }
public string? TypeName { get; }
public string? Description { get; }
public string? DisplayName { get; }
public string? Category { get; }
}

View file

@ -11,6 +11,7 @@ public class WorkflowDefinitionBuilder : IWorkflowDefinitionBuilder
public int Version { get; private set; } = 1;
public IActivity? Root { get; private set; }
public ICollection<Variable> Variables { get; set; } = new List<Variable>();
public IDictionary<string, object> Metadata { get; set; } = new Dictionary<string, object>();
public IDictionary<string, object> ApplicationProperties { get; set; } = new Dictionary<string, object>();
public IWorkflowDefinitionBuilder WithId(string id)
@ -43,6 +44,12 @@ public class WorkflowDefinitionBuilder : IWorkflowDefinitionBuilder
return this;
}
public IWorkflowDefinitionBuilder WithMetadata(string name, object value)
{
Metadata[name] = value;
return this;
}
public IWorkflowDefinitionBuilder WithApplicationProperty(string name, object value)
{
ApplicationProperties[name] = value;
@ -57,6 +64,6 @@ public class WorkflowDefinitionBuilder : IWorkflowDefinitionBuilder
var identity = new WorkflowIdentity(definitionId, Version, id);
var publication = WorkflowPublication.LatestAndPublished;
var metadata = new WorkflowMetadata();
return new Workflow(identity, publication, metadata, root, Variables, ApplicationProperties);
return new Workflow(identity, publication, metadata, root, Variables, Metadata, ApplicationProperties);
}
}

View file

@ -12,6 +12,7 @@ public abstract class Activity : IActivity
public string TypeName { get; set; }
public bool CanStartWorkflow { get; set; }
public IDictionary<string, object> ApplicationProperties { get; set; } = new Dictionary<string, object>();
public IDictionary<string, object> Metadata { get; set; } = new Dictionary<string, object>();
protected virtual ValueTask ExecuteAsync(ActivityExecutionContext context)
{

View file

@ -8,20 +8,29 @@ public class Workflow : Composite, ICloneable
public Workflow(
WorkflowIdentity identity,
WorkflowPublication publication,
WorkflowMetadata metadata,
WorkflowMetadata workflowMetadata,
IActivity root,
ICollection<Variable> variables,
IDictionary<string, object> metadata,
IDictionary<string, object> applicationProperties)
{
Identity = identity;
Publication = publication;
Metadata = metadata;
WorkflowMetadata = workflowMetadata;
Variables = variables;
Metadata = metadata;
ApplicationProperties = applicationProperties;
Root = root;
}
public static Workflow FromActivity(IActivity root) => new(WorkflowIdentity.VersionOne, WorkflowPublication.LatestDraft, new WorkflowMetadata(), root, new List<Variable>(), new Dictionary<string, object>());
public static Workflow FromActivity(IActivity root) => new(
WorkflowIdentity.VersionOne,
WorkflowPublication.LatestDraft,
new WorkflowMetadata(),
root,
new List<Variable>(),
new Dictionary<string, object>(),
new Dictionary<string, object>());
/// <summary>
/// Creates a new memory register initialized with this workflow's variables.
@ -35,7 +44,7 @@ public class Workflow : Composite, ICloneable
public WorkflowIdentity Identity { get; set; }
public WorkflowPublication Publication { get; set; }
public WorkflowMetadata Metadata { get; set; }
public WorkflowMetadata WorkflowMetadata { get; set; }
public ICollection<Variable> Variables { get; init; }
public Workflow Clone() => (Workflow)((ICloneable)this).Clone();
object ICloneable.Clone() => MemberwiseClone();

View file

@ -27,15 +27,16 @@ public class ActivityDescriber : IActivityDescriber
public ValueTask<ActivityDescriptor> DescribeActivityAsync(Type activityType, CancellationToken cancellationToken = default)
{
var ns = TypeNameHelper.GenerateNamespace(activityType);
var typeName = activityType.Name;
var fullTypeName = TypeNameHelper.GenerateTypeName(activityType, ns);
var activityAttr = activityType.GetCustomAttribute<ActivityAttribute>();
var ns = activityAttr?.Namespace ?? TypeNameHelper.GenerateNamespace(activityType);
var typeName = activityAttr?.TypeName ?? activityType.Name;
var fullTypeName = $"{ns}.{typeName}";
var displayNameAttr = activityType.GetCustomAttribute<DisplayNameAttribute>();
var displayName = displayNameAttr?.DisplayName ?? typeName.Humanize(LetterCasing.Title);
var displayName = displayNameAttr?.DisplayName ?? activityAttr?.DisplayName ?? typeName.Humanize(LetterCasing.Title);
var categoryAttr = activityType.GetCustomAttribute<CategoryAttribute>();
var category = categoryAttr?.Category ?? TypeNameHelper.GetCategoryFromNamespace(ns) ?? "Miscellaneous";
var category = categoryAttr?.Category ?? activityAttr?.Category ?? TypeNameHelper.GetCategoryFromNamespace(ns) ?? "Miscellaneous";
var descriptionAttr = activityType.GetCustomAttribute<DescriptionAttribute>();
var description = descriptionAttr?.Description;
var description = descriptionAttr?.Description ?? activityAttr?.Description;
var outboundPorts =
from prop in activityType.GetProperties(BindingFlags.Public | BindingFlags.Instance)

View file

@ -35,6 +35,7 @@ namespace Elsa.Management.Services
new WorkflowMetadata(CreatedAt: _systemClock.UtcNow),
new Sequence(),
new List<Variable>(),
new Dictionary<string, object>(),
new Dictionary<string, object>());
}

View file

@ -69,6 +69,7 @@ export class FlowchartComponent implements ContainerActivityComponent {
const activity: Activity = {
id: uuid(),
typeName: descriptor.activityType,
applicationProperties: {},
metadata: {
designer: {
position: {
@ -181,6 +182,7 @@ export class FlowchartComponent implements ContainerActivityComponent {
id: this.rootId,
start: first(activities)?.id,
metadata: {},
applicationProperties: {},
variables: []
} as Flowchart;
}

View file

@ -185,7 +185,6 @@ export class WorkflowEditor {
private getWorkflowInternal = async (): Promise<Workflow> => {
const root = await this.canvas.exportGraph();
const workflow = this.workflow;
workflow.root = root;
return workflow;
};

View file

@ -6,6 +6,7 @@ export interface Activity {
id: string;
typeName: string;
metadata: any;
applicationProperties: any;
[name: string]: any;
}

View file

@ -10,7 +10,7 @@ public record Connection(IActivity Source, IActivity Target, string? SourcePort,
[Activity("Elsa", "Workflows", "A flowchart is a collection of activities and connections between them.")]
public class Flowchart : Container
{
[Node] public IActivity? Start { get; set; } = default!;
[Node] public IActivity? Start { get; set; }
public ICollection<Connection> Connections { get; set; } = new List<Connection>();
protected override void ScheduleChildren(ActivityExecutionContext context)

View file

@ -20,12 +20,12 @@ public class FlowchartJsonConverter : JsonConverter<Flowchart>
throw new JsonException("Failed to parse JsonDocument");
var connectionsElement = doc.RootElement.GetProperty("connections");
var applicationPropertiesElement = doc.RootElement.GetProperty("applicationProperties");
var metadataElement = doc.RootElement.GetProperty("metadata");
var activitiesElement = doc.RootElement.GetProperty("activities");
var id = doc.RootElement.GetProperty("id").GetString()!;
var startId = doc.RootElement.TryGetProperty("start", out var startElement) ? startElement.GetString() : default;
var rootActivities = activitiesElement.Deserialize<ICollection<IActivity>>(options) ?? new List<IActivity>();
var applicationProperties = applicationPropertiesElement.Deserialize<IDictionary<string, object>>(options) ?? new Dictionary<string, object>();
var metadata = metadataElement.Deserialize<IDictionary<string, object>>(options) ?? new Dictionary<string, object>();
var start = rootActivities.FirstOrDefault(x => x.Id == startId) ?? rootActivities.FirstOrDefault();
var connectionSerializerOptions = new JsonSerializerOptions(options);
var activities = WalkActivityTree(rootActivities).ToList();
@ -37,7 +37,7 @@ public class FlowchartJsonConverter : JsonConverter<Flowchart>
return new Flowchart
{
Id = id,
ApplicationProperties = applicationProperties,
Metadata = metadata,
Activities = rootActivities,
Connections = connections,
Start = start,
@ -55,6 +55,7 @@ public class FlowchartJsonConverter : JsonConverter<Flowchart>
{
value.TypeName,
value.Id,
value.Metadata,
value.ApplicationProperties,
Start = value.Start?.Id,
value.Activities,

View file

@ -15,6 +15,7 @@ public class WorkflowDefinition : Entity
public int Version { get; set; } = 1;
public IActivity Root { get; set; } = default!;
public ICollection<Variable> Variables { get; set; } = new List<Variable>();
public IDictionary<string, object> Metadata { get; set; } = new Dictionary<string, object>();
public IDictionary<string, object> ApplicationProperties { get; set; } = new Dictionary<string, object>();
public bool IsLatest { get; set; }
public bool IsPublished { get; set; }

View file

@ -16,6 +16,7 @@ public class WorkflowDefinitionMapper
new WorkflowMetadata(definition.Name, definition.Description, definition.CreatedAt),
definition.Root,
definition.Variables,
definition.Metadata,
definition.ApplicationProperties);
}
@ -25,7 +26,7 @@ public class WorkflowDefinitionMapper
{
var identity = workflow.Identity;
var publication = workflow.Publication;
var metadata = workflow.Metadata;
var metadata = workflow.WorkflowMetadata;
definition.Id = identity.Id;
definition.DefinitionId = identity.DefinitionId;
@ -37,6 +38,7 @@ public class WorkflowDefinitionMapper
definition.IsLatest = publication.IsLatest;
definition.IsPublished = publication.IsPublished;
definition.Variables = workflow.Variables;
definition.Metadata = workflow.Metadata;
definition.ApplicationProperties = workflow.ApplicationProperties;
return definition;

View file

@ -10,6 +10,7 @@ namespace Elsa.Persistence.EntityFrameworkCore.Configuration
{
builder.Ignore(x => x.Root);
builder.Ignore(x => x.Variables);
builder.Ignore(x => x.Metadata);
builder.Ignore(x => x.ApplicationProperties);
builder.Property<string>("Data");

View file

@ -22,6 +22,7 @@ public class WorkflowDefinitionSerializer : IEntitySerializer<WorkflowDefinition
{
entity.Root,
entity.Variables,
entity.Metadata,
entity.ApplicationProperties
};
@ -33,7 +34,7 @@ public class WorkflowDefinitionSerializer : IEntitySerializer<WorkflowDefinition
public void Deserialize(ElsaDbContext dbContext, WorkflowDefinition entity)
{
var data = new WorkflowDefinitionState(entity.Root, entity.Variables, entity.ApplicationProperties);
var data = new WorkflowDefinitionState(entity.Root, entity.Variables, entity.Metadata, entity.ApplicationProperties);
var json = (string?) dbContext.Entry(entity).Property("Data").CurrentValue;
if (!string.IsNullOrWhiteSpace(json))
@ -44,6 +45,7 @@ public class WorkflowDefinitionSerializer : IEntitySerializer<WorkflowDefinition
entity.Root = data.Root;
entity.Variables = data.Variables;
entity.Metadata = data.Metadata;
entity.ApplicationProperties = data.ApplicationProperties;
}
@ -54,15 +56,17 @@ public class WorkflowDefinitionSerializer : IEntitySerializer<WorkflowDefinition
{
}
public WorkflowDefinitionState(IActivity root, ICollection<Variable> variables, IDictionary<string, object> applicationProperties)
public WorkflowDefinitionState(IActivity root, ICollection<Variable> variables, IDictionary<string, object> metadata, IDictionary<string, object> applicationProperties)
{
Root = root;
Variables = variables;
Metadata = metadata;
ApplicationProperties = applicationProperties;
}
public IActivity Root { get; init; } = default!;
public ICollection<Variable> Variables { get; set; } = new List<Variable>();
public IDictionary<string, object> Metadata { get; set; } = new Dictionary<string, object>();
public IDictionary<string, object> ApplicationProperties { get; set; } = new Dictionary<string, object>();
}
}

View file

@ -33,7 +33,7 @@ public class ConfigurationWorkflowProvider : IWorkflowProvider
public ValueTask<Workflow?> FindByNameAsync(string name, VersionOptions versionOptions, CancellationToken cancellationToken = default)
{
var result = _workflows.FirstOrDefault(x => x.Metadata.Name == name && x.WithVersion(versionOptions));
var result = _workflows.FirstOrDefault(x => x.WorkflowMetadata.Name == name && x.WithVersion(versionOptions));
return ValueTask.FromResult(result);
}