Fix switch activity designer support

This commit is contained in:
Sipke Schoorstra 2022-07-07 14:01:38 +02:00
parent 0a3070d861
commit 50f53bb52b
6 changed files with 71 additions and 25 deletions

View file

@ -4,18 +4,18 @@ import {Container} from "typedi";
import {PanelPosition, PanelStateChangedArgs} from '../panel/models';
import {
Activity,
ActivityDeletedArgs,
ActivityDescriptor,
ActivitySelectedArgs,
ChildActivitySelectedArgs,
Container as ContainerActivity,
ContainerSelectedArgs,
EditChildActivityArgs,
GraphUpdatedArgs,
WorkflowDefinition,
Container as ContainerActivity,
ActivityDeletedArgs
WorkflowDefinition
} from '../../../models';
import {ActivityIdUpdatedArgs, ActivityUpdatedArgs, DeleteActivityRequestedArgs} from './activity-properties-editor';
import {ActivityDriverRegistry, ActivityNameFormatter, ActivityNode, createActivityMap, createActivityNodeMap, EventBus, flatten, flattenList, PluginRegistry, walkActivities} from '../../../services';
import {ActivityDriverRegistry, ActivityNameFormatter, ActivityNode, createActivityMap, createActivityNodeMap, EventBus, flatten, flattenList, PluginRegistry, PortProviderRegistry, walkActivities} from '../../../services';
import {MonacoEditorSettings} from "../../../services/monaco-editor-settings";
import {Flowchart} from "../../activities/flowchart/models";
import {ActivityPropertyChangedEventArgs, WorkflowDefinitionPropsUpdatedArgs, WorkflowDefinitionUpdatedArgs, WorkflowEditorEventTypes} from "./models";
@ -34,6 +34,7 @@ export class WorkflowDefinitionEditor {
private readonly pluginRegistry: PluginRegistry;
private readonly eventBus: EventBus;
private readonly activityNameFormatter: ActivityNameFormatter;
private readonly portProviderRegistry: PortProviderRegistry;
private canvas: HTMLElsaCanvasElement;
private container: HTMLDivElement;
private toolbox: HTMLElsaWorkflowDefinitionEditorToolboxElement;
@ -42,13 +43,16 @@ export class WorkflowDefinitionEditor {
private applyActivityChanges: (activity: Activity) => void;
private deleteActivity: (activity: Activity) => void;
private readonly emitActivityChangedDebounced: (e: ActivityPropertyChangedEventArgs) => void;
private readonly updateModelDebounced: () => void;
private readonly saveChangesDebounced: () => void;
constructor() {
this.eventBus = Container.get(EventBus);
this.pluginRegistry = Container.get(PluginRegistry);
this.activityNameFormatter = Container.get(ActivityNameFormatter);
this.portProviderRegistry = Container.get(PortProviderRegistry);
this.emitActivityChangedDebounced = debounce(this.emitActivityChanged, 100);
this.updateModelDebounced = debounce(this.updateModel, 10);
this.saveChangesDebounced = debounce(this.saveChanges, 1000);
}
@ -110,6 +114,7 @@ export class WorkflowDefinitionEditor {
@Listen('graphUpdated')
private async handleGraphUpdated(e: CustomEvent<GraphUpdatedArgs>) {
this.updateModelDebounced();
this.saveChangesDebounced();
}
@ -119,16 +124,18 @@ export class WorkflowDefinitionEditor {
const currentActivityId = this.currentWorkflowPath[this.currentWorkflowPath.length - 1].activityId;
const currentActivity = this.nodeMap[currentActivityId];
const parentActivity = this.nodeMap[parentActivityId];
const indexInParent = currentActivity.activities.findIndex(x => x == parentActivity);
const parentActivityDescriptor = descriptorsStore.activityDescriptors.find(x => x.activityType == parentActivity.typeName);
const indexInParent = currentActivity.activities?.findIndex(x => x == parentActivity);
const portName = e.detail.port.name;
const item: WorkflowNavigationItem = {
activityId: parentActivityId,
portName: e.detail.port.name,
portName: portName,
index: indexInParent
};
const portName = camelCase(e.detail.port.name);
const activityProperty = parentActivity[portName] as Activity | Array<Activity>;
const portProvider = this.portProviderRegistry.get(parentActivity.typeName);
const activityProperty = portProvider.resolvePort(portName, {activity: parentActivity, activityDescriptor: parentActivityDescriptor });
const isContainer = Array.isArray(activityProperty);
if (!activityProperty) {
@ -299,17 +306,17 @@ export class WorkflowDefinitionEditor {
}
if (!!currentPortName) {
const portName = camelCase(currentPortName);
if (currentActivityDescriptor.isContainer) {
const parentNavigationItem = this.currentWorkflowPath[this.currentWorkflowPath.length - 2];
const parentActivityId = parentNavigationItem.activityId;
const parentActivity = this.nodeMap[parentActivityId] as ContainerActivity;
const parentActivitiesProp = parentActivity[portName] as Array<Activity>;
const parentActivityDescriptor = descriptorsStore.activityDescriptors.find(x => x.activityType == parentActivity.typeName);
const portProvider = this.portProviderRegistry.get(parentActivity.typeName);
const parentActivitiesProp = portProvider.resolvePort(currentPortName, {activity: parentActivity, activityDescriptor: parentActivityDescriptor}) as Array<Activity>;
parentActivitiesProp[currentWorkflowNavigationItem.index] = activity;
} else {
debugger;
currentActivity[portName] = activity;
const portProvider = this.portProviderRegistry.get(currentActivity.typeName);
portProvider.assignPort(currentPortName, activity, {activity: currentActivity, activityDescriptor: currentActivityDescriptor});
}
} else {
workflowDefinition.root = activity;
@ -323,9 +330,12 @@ export class WorkflowDefinitionEditor {
await this.eventBus.emit(WorkflowEditorEventTypes.Activity.PropertyChanged, this, activity, propertyName, this);
};
private saveChanges = async (): Promise<void> => {
private updateModel = async (): Promise<void> => {
const workflowDefinition = await this.getWorkflowDefinitionInternal();
await this.updateWorkflowDefinition(workflowDefinition);
};
private saveChanges = async (): Promise<void> => {
this.workflowUpdated.emit({workflowDefinition: this.workflowDefinitionState});
};
@ -365,7 +375,14 @@ export class WorkflowDefinitionEditor {
if (!currentItem)
return this.workflowDefinitionState.root;
return this.nodeMap[currentItem.activityId];
const activity = this.nodeMap[currentItem.activityId];
const activityDescriptor = descriptorsStore.activityDescriptors.find(x => x.activityType == activity.typeName);
if(activityDescriptor.isContainer)
return activity;
const portProvider = this.portProviderRegistry.get(activity.typeName);
return portProvider.resolvePort(currentItem.portName, {activity, activityDescriptor}) as Activity;
};
private createNodeMap = (workflowDefinition: WorkflowDefinition): void => {
@ -395,8 +412,6 @@ export class WorkflowDefinitionEditor {
});
this.nodeMap[newName] = activity;
requestAnimationFrame(() => {});
};
private onZoomToFit = async () => await this.canvas.zoomToFit()
@ -405,7 +420,7 @@ export class WorkflowDefinitionEditor {
const updatedActivity = e.detail.activity;
this.nodeMap[updatedActivity.id] = updatedActivity;
if(!!this.applyActivityChanges)
if (!!this.applyActivityChanges)
this.applyActivityChanges(updatedActivity);
this.emitActivityChangedDebounced({...e.detail, workflowEditor: this.el});

View file

@ -1,6 +1,6 @@
import {Component, FunctionalComponent, h, Prop, Event, EventEmitter} from "@stencil/core";
import {Container} from "typedi";
import {ActivityIconRegistry, ActivityNode, flatten, walkActivities} from "../../../services";
import {ActivityIconRegistry, ActivityNode, flatten, PortProviderRegistry, walkActivities} from "../../../services";
import {WorkflowNavigationItem} from "./models";
import {Port, WorkflowDefinition} from "../../../models";
import descriptorsStore from "../../../data/descriptors-store";
@ -10,6 +10,13 @@ import descriptorsStore from "../../../data/descriptors-store";
shadow: false
})
export class WorkflowNavigator {
private readonly iconRegistry: ActivityIconRegistry;
private readonly portProviderRegistry: PortProviderRegistry;
constructor() {
this.iconRegistry = Container.get(ActivityIconRegistry);
this.portProviderRegistry = Container.get(PortProviderRegistry);
}
@Prop() items: Array<WorkflowNavigationItem> = [];
@Prop() workflowDefinition: WorkflowDefinition;
@ -37,11 +44,10 @@ export class WorkflowNavigator {
}
private renderPathItem = (item: WorkflowNavigationItem, index: number, nodes: Array<ActivityNode>) => {
const iconRegistry = Container.get(ActivityIconRegistry);
const activityId = item.activityId;
const activity = nodes.find(x => x.activity.id == activityId).activity;
const activityDescriptor = descriptorsStore.activityDescriptors.find(x => x.activityType == activity.typeName);
const icon = iconRegistry.get(activity.typeName)();
const icon = this.iconRegistry.get(activity.typeName)();
const listElements = [];
const isLastItem = index == this.items.length - 1;
@ -53,7 +59,8 @@ export class WorkflowNavigator {
let port: Port = null;
if (!!item.portName) {
const ports = activityDescriptor.ports;
const portProvider = this.portProviderRegistry.get(activity.typeName);
const ports = portProvider.getOutboundPorts({activity, activityDescriptor});
if (ports.length > 1)
port = ports.find(x => x.name == item.portName);

View file

@ -1,5 +1,6 @@
import 'reflect-metadata';
import {Service} from "typedi";
import {camelCase} from 'lodash';
import {Activity, Port, PortMode} from "../../models";
import {SwitchActivity, SwitchCase} from "./models";
import {PortProvider, PortProviderContext} from "../../services";
@ -31,4 +32,15 @@ export class SwitchPortProvider implements PortProvider {
return caseItem.activity;
}
assignPort(portName: string, activity: Activity, context: PortProviderContext) {
const switchActivity = context.activity as SwitchActivity;
const cases: Array<SwitchCase> = switchActivity.cases ?? [];
const caseItem = cases.find(x => x.label == portName);
if(!caseItem)
return null;
caseItem.activity = activity;
}
}

View file

@ -7,7 +7,6 @@ import {Activity, Port} from "../models";
@Service()
export class DefaultPortProvider implements PortProvider {
getInboundPorts(context: PortProviderContext): Array<Port> {
const {activityDescriptor} = context;
return [];
}
@ -20,10 +19,20 @@ export class DefaultPortProvider implements PortProvider {
const propName = camelCase(portName);
const activity = context.activity;
if(!activity)
if (!activity)
return null;
return activity[propName] as Activity | Array<Activity>;
}
assignPort(portName: string, activity: Activity, context: PortProviderContext) {
const propName = camelCase(portName);
const container = context.activity;
if (!container)
return null;
container[propName] = activity;
}
}

View file

@ -6,6 +6,8 @@ export interface PortProvider {
getOutboundPorts(context: PortProviderContext): Array<Port>;
resolvePort(portName: string, context: PortProviderContext): Activity | Array<Activity>;
assignPort(portName: string, activity: Activity, context: PortProviderContext);
}
export interface PortProviderContext {

View file

@ -3,6 +3,7 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using Dahomey.Json;
using Dahomey.Json.Serialization.Conventions;
using Dahomey.Json.Util;
using Elsa.Expressions.Exceptions;
using DahomeyJsonNode = System.Text.Json.JsonNode;
@ -55,7 +56,7 @@ public static class ObjectConverter
var targetTypeConverter = TypeDescriptor.GetConverter(underlyingTargetType);
if (targetTypeConverter.CanConvertFrom(underlyingSourceType))
return targetTypeConverter.ConvertFrom(value);
return targetTypeConverter.IsValid(value) ? targetTypeConverter.ConvertFrom(value) : targetType.GetDefaultValue();
var sourceTypeConverter = TypeDescriptor.GetConverter(underlyingSourceType);