Fix event bus handler disposal
This commit is contained in:
parent
3e386d0529
commit
89e1dc4e67
|
|
@ -44,24 +44,35 @@ export class ElsaWorkflowDesigner {
|
|||
this.removeActivity(e.detail.activityId);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
eventBus.on(EventTypes.ActivityPicked, this.onActivityPicked);
|
||||
eventBus.on(EventTypes.UpdateActivity, this.onUpdateActivity);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
eventBus.off(EventTypes.ActivityPicked, this.onActivityPicked);
|
||||
eventBus.off(EventTypes.UpdateActivity, this.onUpdateActivity);
|
||||
cleanup();
|
||||
}
|
||||
|
||||
componentWillLoad() {
|
||||
this.workflowModel = this.model;
|
||||
|
||||
eventBus.on(EventTypes.ActivityPicked, async args => {
|
||||
const activityDescriptor = (args as ActivityDescriptor);
|
||||
const connectFromRoot = !this.parentActivityOutcome || this.parentActivityOutcome == "";
|
||||
const sourceId = connectFromRoot ? null : this.parentActivityId;
|
||||
const targetId = connectFromRoot ? this.parentActivityId : null;
|
||||
const activityModel = this.addActivity(activityDescriptor, sourceId, targetId, this.parentActivityOutcome);
|
||||
|
||||
this.showActivityEditor(activityModel, false);
|
||||
});
|
||||
|
||||
eventBus.on(EventTypes.UpdateActivity, async args => {
|
||||
const activityModel = (args as ActivityModel);
|
||||
this.updateActivity(activityModel);
|
||||
});
|
||||
}
|
||||
|
||||
onActivityPicked = async (args) =>{
|
||||
const activityDescriptor = (args as ActivityDescriptor);
|
||||
const connectFromRoot = !this.parentActivityOutcome || this.parentActivityOutcome == "";
|
||||
const sourceId = connectFromRoot ? null : this.parentActivityId;
|
||||
const targetId = connectFromRoot ? this.parentActivityId : null;
|
||||
const activityModel = this.addActivity(activityDescriptor, sourceId, targetId, this.parentActivityOutcome);
|
||||
|
||||
this.showActivityEditor(activityModel, false);
|
||||
};
|
||||
|
||||
onUpdateActivity = args => {
|
||||
const activityModel = (args as ActivityModel);
|
||||
this.updateActivity(activityModel);
|
||||
};
|
||||
|
||||
componentWillRender() {
|
||||
destroy();
|
||||
|
|
@ -106,10 +117,6 @@ export class ElsaWorkflowDesigner {
|
|||
this.removeInvalidConnections(invalidConnections);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
showActivityPicker() {
|
||||
eventBus.emit(EventTypes.ShowActivityPicker);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,33 +3,41 @@ import {eventBus} from '../../../../services/event-bus';
|
|||
import {EventTypes, WorkflowDefinition} from "../../../../models";
|
||||
|
||||
@Component({
|
||||
tag: 'elsa-workflow-definition-editor-notifications',
|
||||
shadow: false,
|
||||
tag: 'elsa-workflow-definition-editor-notifications',
|
||||
shadow: false,
|
||||
})
|
||||
export class ElsaWorkflowEditorNotifications {
|
||||
|
||||
toastNotificationElement: HTMLElsaToastNotificationElement;
|
||||
toastNotificationElement: HTMLElsaToastNotificationElement;
|
||||
|
||||
componentDidLoad() {
|
||||
eventBus.on(EventTypes.WorkflowPublished, async (workflowDefinition: WorkflowDefinition) => {
|
||||
await this.toastNotificationElement.show({autoCloseIn: 1500, title: 'Workflow Published', message: `Workflow successfully published at version ${workflowDefinition.version}.`});
|
||||
connectedCallback() {
|
||||
eventBus.on(EventTypes.WorkflowPublished, this.onWorkflowPublished);
|
||||
eventBus.on(EventTypes.WorkflowRetracted, this.onWorkflowRetracted);
|
||||
eventBus.on(EventTypes.WorkflowImported, this.onWorkflowImported);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
eventBus.off(EventTypes.WorkflowPublished, this.onWorkflowPublished);
|
||||
eventBus.off(EventTypes.WorkflowRetracted, this.onWorkflowRetracted);
|
||||
eventBus.off(EventTypes.WorkflowImported, this.onWorkflowImported);
|
||||
}
|
||||
|
||||
onWorkflowPublished = async (workflowDefinition: WorkflowDefinition) => await this.toastNotificationElement.show({autoCloseIn: 1500, title: 'Workflow Unpublished', message: `Workflow successfully retracted.`});
|
||||
|
||||
onWorkflowRetracted = async (workflowDefinition: WorkflowDefinition) => await this.toastNotificationElement.show({
|
||||
autoCloseIn: 1500,
|
||||
title: 'Workflow Published',
|
||||
message: `Workflow successfully published at version ${workflowDefinition.version}.`
|
||||
});
|
||||
|
||||
eventBus.on(EventTypes.WorkflowRetracted, async (workflowDefinition: WorkflowDefinition) => {
|
||||
await this.toastNotificationElement.show({autoCloseIn: 1500, title: 'Workflow Unpublished', message: `Workflow successfully retracted.`});
|
||||
});
|
||||
onWorkflowImported = async (workflowDefinition: WorkflowDefinition) => await this.toastNotificationElement.show({autoCloseIn: 1500, title: 'Workflow Imported', message: `Workflow successfully imported.`});
|
||||
|
||||
eventBus.on(EventTypes.WorkflowImported, async (workflowDefinition: WorkflowDefinition) => {
|
||||
await this.toastNotificationElement.show({autoCloseIn: 1500, title: 'Workflow Imported', message: `Workflow successfully imported.`});
|
||||
});
|
||||
}
|
||||
render() {
|
||||
|
||||
render() {
|
||||
|
||||
return (
|
||||
<Host class="block">
|
||||
<elsa-toast-notification ref={el => this.toastNotificationElement = el}/>
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Host class="block">
|
||||
<elsa-toast-notification ref={el => this.toastNotificationElement = el}/>
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,406 +8,415 @@ import Tunnel, {WorkflowEditorState} from '../../../../data/workflow-editor';
|
|||
import {downloadFromBlob} from "../../../../utils/download";
|
||||
|
||||
@Component({
|
||||
tag: 'elsa-workflow-definition-editor-screen',
|
||||
styleUrl: 'elsa-workflow-definition-editor-screen.css',
|
||||
shadow: false,
|
||||
tag: 'elsa-workflow-definition-editor-screen',
|
||||
styleUrl: 'elsa-workflow-definition-editor-screen.css',
|
||||
shadow: false,
|
||||
})
|
||||
export class ElsaWorkflowDefinitionEditorScreen {
|
||||
|
||||
constructor() {
|
||||
pluginManager.initialize();
|
||||
}
|
||||
|
||||
@Event() workflowSaved: EventEmitter<WorkflowDefinition>;
|
||||
@Prop({attribute: 'workflow-definition-id', reflect: true}) workflowDefinitionId: string;
|
||||
@Prop({attribute: 'server-url', reflect: true}) serverUrl: string;
|
||||
@Prop({attribute: 'monaco-lib-path', reflect: true}) monacoLibPath: string;
|
||||
@State() workflowDefinition: WorkflowDefinition;
|
||||
@State() workflowModel: WorkflowModel;
|
||||
@State() publishing: boolean;
|
||||
@State() unPublishing: boolean;
|
||||
@State() unPublished: boolean;
|
||||
@State() saving: boolean;
|
||||
@State() saved: boolean;
|
||||
@State() importing: boolean;
|
||||
@State() imported: boolean;
|
||||
@State() networkError: string;
|
||||
el: HTMLElement;
|
||||
designer: HTMLElsaDesignerTreeElement;
|
||||
|
||||
@Method()
|
||||
async getServerUrl(): Promise<string> {
|
||||
return this.serverUrl;
|
||||
}
|
||||
|
||||
@Method()
|
||||
async getWorkflowDefinitionId(): Promise<string> {
|
||||
return this.workflowDefinition.definitionId;
|
||||
}
|
||||
|
||||
@Method()
|
||||
async exportWorkflow() {
|
||||
const client = createElsaClient(this.serverUrl);
|
||||
const workflowDefinition = this.workflowDefinition;
|
||||
const versionOptions: VersionOptions = {version: workflowDefinition.version};
|
||||
const response = await client.workflowDefinitionsApi.export(workflowDefinition.definitionId, versionOptions);
|
||||
downloadFromBlob(response.data, {contentType: 'application/json', fileName: response.fileName});
|
||||
}
|
||||
|
||||
@Method()
|
||||
async importWorkflow(file: File) {
|
||||
const client = createElsaClient(this.serverUrl);
|
||||
|
||||
this.importing = true;
|
||||
this.imported = false;
|
||||
this.networkError = null;
|
||||
|
||||
// A small hack to make sure JS Plumb is cleaned up before HTML elements get removed.
|
||||
await this.designer.destroyJsPlumb();
|
||||
|
||||
try {
|
||||
const workflowDefinition = await client.workflowDefinitionsApi.import(this.workflowDefinition.definitionId, file);
|
||||
this.workflowDefinition = workflowDefinition;
|
||||
this.workflowModel = this.mapWorkflowModel(workflowDefinition);
|
||||
|
||||
this.importing = false;
|
||||
this.imported = true;
|
||||
setTimeout(() => this.imported = false, 500);
|
||||
eventBus.emit(EventTypes.WorkflowImported, this, this.workflowDefinition);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.importing = false;
|
||||
this.imported = false;
|
||||
this.networkError = e.message;
|
||||
setTimeout(() => this.networkError = null, 10000);
|
||||
}
|
||||
}
|
||||
|
||||
@Watch('workflowDefinitionId')
|
||||
async workflowDefinitionIdChangedHandler(newValue: string) {
|
||||
const workflowDefinitionId = newValue;
|
||||
let workflowDefinition: WorkflowDefinition = this.createWorkflowDefinition();
|
||||
const client = createElsaClient(this.serverUrl);
|
||||
|
||||
if (workflowDefinitionId && workflowDefinitionId.length > 0) {
|
||||
try {
|
||||
workflowDefinition = await client.workflowDefinitionsApi.getByDefinitionAndVersion(workflowDefinitionId, {isLatest: true});
|
||||
} catch {
|
||||
console.warn(`The specified workflow definition does not exist. Creating a new one.`)
|
||||
}
|
||||
constructor() {
|
||||
pluginManager.initialize();
|
||||
}
|
||||
|
||||
this.updateWorkflowDefinition(workflowDefinition);
|
||||
}
|
||||
@Event() workflowSaved: EventEmitter<WorkflowDefinition>;
|
||||
@Prop({attribute: 'workflow-definition-id', reflect: true}) workflowDefinitionId: string;
|
||||
@Prop({attribute: 'server-url', reflect: true}) serverUrl: string;
|
||||
@Prop({attribute: 'monaco-lib-path', reflect: true}) monacoLibPath: string;
|
||||
@State() workflowDefinition: WorkflowDefinition;
|
||||
@State() workflowModel: WorkflowModel;
|
||||
@State() publishing: boolean;
|
||||
@State() unPublishing: boolean;
|
||||
@State() unPublished: boolean;
|
||||
@State() saving: boolean;
|
||||
@State() saved: boolean;
|
||||
@State() importing: boolean;
|
||||
@State() imported: boolean;
|
||||
@State() networkError: string;
|
||||
el: HTMLElement;
|
||||
designer: HTMLElsaDesignerTreeElement;
|
||||
|
||||
@Watch("serverUrl")
|
||||
async serverUrlChangedHandler(newValue: string) {
|
||||
if (newValue && newValue.length > 0)
|
||||
await this.loadActivityDescriptors();
|
||||
}
|
||||
|
||||
@Watch("monacoLibPath")
|
||||
async monacoLibPathChangedHandler(newValue: string) {
|
||||
state.monacoLibPath = newValue;
|
||||
}
|
||||
|
||||
@Listen('workflow-changed')
|
||||
async workflowChangedHandler(event: CustomEvent<WorkflowModel>) {
|
||||
const workflowModel = event.detail;
|
||||
await this.saveWorkflowInternal(workflowModel);
|
||||
}
|
||||
|
||||
async componentWillLoad() {
|
||||
await this.serverUrlChangedHandler(this.serverUrl);
|
||||
await this.workflowDefinitionIdChangedHandler(this.workflowDefinitionId);
|
||||
await this.monacoLibPathChangedHandler(this.monacoLibPath);
|
||||
}
|
||||
|
||||
componentDidLoad() {
|
||||
if (!this.designer) {
|
||||
this.designer = this.el.querySelector("elsa-designer-tree") as HTMLElsaDesignerTreeElement;
|
||||
this.designer.model = this.workflowModel;
|
||||
@Method()
|
||||
async getServerUrl(): Promise<string> {
|
||||
return this.serverUrl;
|
||||
}
|
||||
|
||||
eventBus.on(EventTypes.UpdateWorkflowSettings, async (workflowDefinition: WorkflowDefinition) => {
|
||||
this.updateWorkflowDefinition(workflowDefinition);
|
||||
await this.saveWorkflowInternal(this.workflowModel);
|
||||
});
|
||||
}
|
||||
|
||||
async loadActivityDescriptors() {
|
||||
const client = createElsaClient(this.serverUrl);
|
||||
state.activityDescriptors = await client.activitiesApi.list();
|
||||
}
|
||||
|
||||
updateWorkflowDefinition(value: WorkflowDefinition) {
|
||||
this.workflowDefinition = value;
|
||||
this.workflowModel = this.mapWorkflowModel(value);
|
||||
}
|
||||
|
||||
async publishWorkflow() {
|
||||
this.publishing = true;
|
||||
await this.saveWorkflow(true);
|
||||
this.publishing = false;
|
||||
eventBus.emit(EventTypes.WorkflowPublished, this, this.workflowDefinition);
|
||||
}
|
||||
|
||||
async unPublishWorkflow() {
|
||||
this.unPublishing = true;
|
||||
await this.unpublishWorkflow();
|
||||
this.unPublishing = false;
|
||||
eventBus.emit(EventTypes.WorkflowRetracted, this, this.workflowDefinition);
|
||||
}
|
||||
|
||||
async saveWorkflow(publish?: boolean) {
|
||||
await this.saveWorkflowInternal(null, publish);
|
||||
}
|
||||
|
||||
async saveWorkflowInternal(workflowModel?: WorkflowModel, publish?: boolean) {
|
||||
if (!this.serverUrl || this.serverUrl.length == 0)
|
||||
return;
|
||||
|
||||
workflowModel = workflowModel || this.workflowModel;
|
||||
|
||||
const client = createElsaClient(this.serverUrl);
|
||||
let workflowDefinition = this.workflowDefinition;
|
||||
|
||||
const request: SaveWorkflowDefinitionRequest = {
|
||||
workflowDefinitionId: workflowDefinition.definitionId || this.workflowDefinitionId,
|
||||
contextOptions: workflowDefinition.contextOptions,
|
||||
deleteCompletedInstances: workflowDefinition.deleteCompletedInstances,
|
||||
description: workflowDefinition.description,
|
||||
displayName: workflowDefinition.displayName,
|
||||
isSingleton: workflowDefinition.isSingleton,
|
||||
name: workflowDefinition.name,
|
||||
persistenceBehavior: workflowDefinition.persistenceBehavior,
|
||||
publish: publish || false,
|
||||
variables: workflowDefinition.variables,
|
||||
activities: workflowModel.activities.map<ActivityDefinition>(x => ({
|
||||
activityId: x.activityId,
|
||||
type: x.type,
|
||||
name: x.name,
|
||||
displayName: x.displayName,
|
||||
description: x.description,
|
||||
persistWorkflow: x.persistWorkflow,
|
||||
loadWorkflowContext: x.loadWorkflowContext,
|
||||
saveWorkflowContext: x.saveWorkflowContext,
|
||||
persistOutput: x.persistOutput,
|
||||
properties: x.properties
|
||||
})),
|
||||
connections: workflowModel.connections.map<ConnectionDefinition>(x => ({
|
||||
sourceActivityId: x.sourceId,
|
||||
targetActivityId: x.targetId,
|
||||
outcome: x.outcome
|
||||
})),
|
||||
};
|
||||
|
||||
this.saving = !publish;
|
||||
this.publishing = publish;
|
||||
|
||||
try {
|
||||
workflowDefinition = await client.workflowDefinitionsApi.save(request);
|
||||
this.workflowDefinition = workflowDefinition;
|
||||
this.workflowModel = this.mapWorkflowModel(workflowDefinition);
|
||||
|
||||
this.saving = false;
|
||||
this.saved = !publish;
|
||||
this.publishing = false;
|
||||
setTimeout(() => this.saved = false, 500);
|
||||
this.workflowSaved.emit(workflowDefinition);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.saving = false;
|
||||
this.saved = false;
|
||||
this.networkError = e.message;
|
||||
setTimeout(() => this.networkError = null, 10000);
|
||||
@Method()
|
||||
async getWorkflowDefinitionId(): Promise<string> {
|
||||
return this.workflowDefinition.definitionId;
|
||||
}
|
||||
}
|
||||
|
||||
async unpublishWorkflow() {
|
||||
const client = createElsaClient(this.serverUrl);
|
||||
const workflowDefinitionId = this.workflowDefinition.definitionId;
|
||||
this.unPublishing = true;
|
||||
|
||||
try {
|
||||
this.workflowDefinition = await client.workflowDefinitionsApi.retract(workflowDefinitionId);
|
||||
this.unPublishing = false;
|
||||
this.unPublished = true
|
||||
setTimeout(() => this.unPublished = false, 500);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.unPublishing = false;
|
||||
this.unPublished = false;
|
||||
this.networkError = e.message;
|
||||
setTimeout(() => this.networkError = null, 2000);
|
||||
@Method()
|
||||
async exportWorkflow() {
|
||||
const client = createElsaClient(this.serverUrl);
|
||||
const workflowDefinition = this.workflowDefinition;
|
||||
const versionOptions: VersionOptions = {version: workflowDefinition.version};
|
||||
const response = await client.workflowDefinitionsApi.export(workflowDefinition.definitionId, versionOptions);
|
||||
downloadFromBlob(response.data, {contentType: 'application/json', fileName: response.fileName});
|
||||
}
|
||||
}
|
||||
|
||||
mapWorkflowModel(workflowDefinition: WorkflowDefinition): WorkflowModel {
|
||||
return {
|
||||
activities: workflowDefinition.activities.map(this.mapActivityModel),
|
||||
connections: workflowDefinition.connections.map(this.mapConnectionModel),
|
||||
persistenceBehavior: workflowDefinition.persistenceBehavior,
|
||||
@Method()
|
||||
async importWorkflow(file: File) {
|
||||
const client = createElsaClient(this.serverUrl);
|
||||
|
||||
};
|
||||
}
|
||||
this.importing = true;
|
||||
this.imported = false;
|
||||
this.networkError = null;
|
||||
|
||||
mapActivityModel(source: ActivityDefinition): ActivityModel {
|
||||
const descriptors: Array<ActivityDescriptor> = state.activityDescriptors;
|
||||
const descriptor = descriptors.find(x => x.type == source.type);
|
||||
// A small hack to make sure JS Plumb is cleaned up before HTML elements get removed.
|
||||
await this.designer.destroyJsPlumb();
|
||||
|
||||
return {
|
||||
activityId: source.activityId,
|
||||
description: source.description,
|
||||
displayName: source.displayName,
|
||||
name: source.name,
|
||||
type: source.type,
|
||||
properties: source.properties,
|
||||
outcomes: [...descriptor.outcomes],
|
||||
persistOutput: source.persistOutput,
|
||||
persistWorkflow: source.persistWorkflow,
|
||||
saveWorkflowContext: source.saveWorkflowContext,
|
||||
loadWorkflowContext: source.loadWorkflowContext
|
||||
try {
|
||||
const workflowDefinition = await client.workflowDefinitionsApi.import(this.workflowDefinition.definitionId, file);
|
||||
this.workflowDefinition = workflowDefinition;
|
||||
this.workflowModel = this.mapWorkflowModel(workflowDefinition);
|
||||
|
||||
this.importing = false;
|
||||
this.imported = true;
|
||||
setTimeout(() => this.imported = false, 500);
|
||||
eventBus.emit(EventTypes.WorkflowImported, this, this.workflowDefinition);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.importing = false;
|
||||
this.imported = false;
|
||||
this.networkError = e.message;
|
||||
setTimeout(() => this.networkError = null, 10000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mapConnectionModel(source: ConnectionDefinition): ConnectionModel {
|
||||
return {
|
||||
sourceId: source.sourceActivityId,
|
||||
targetId: source.targetActivityId,
|
||||
outcome: source.outcome
|
||||
@Watch('workflowDefinitionId')
|
||||
async workflowDefinitionIdChangedHandler(newValue: string) {
|
||||
const workflowDefinitionId = newValue;
|
||||
let workflowDefinition: WorkflowDefinition = ElsaWorkflowDefinitionEditorScreen.createWorkflowDefinition();
|
||||
workflowDefinition.definitionId = workflowDefinitionId;
|
||||
const client = createElsaClient(this.serverUrl);
|
||||
|
||||
if (workflowDefinitionId && workflowDefinitionId.length > 0) {
|
||||
try {
|
||||
workflowDefinition = await client.workflowDefinitionsApi.getByDefinitionAndVersion(workflowDefinitionId, {isLatest: true});
|
||||
} catch {
|
||||
console.warn(`The specified workflow definition does not exist. Creating a new one.`)
|
||||
}
|
||||
}
|
||||
|
||||
this.updateWorkflowDefinition(workflowDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
onShowWorkflowSettingsClick() {
|
||||
eventBus.emit(EventTypes.ShowWorkflowSettings);
|
||||
}
|
||||
@Watch("serverUrl")
|
||||
async serverUrlChangedHandler(newValue: string) {
|
||||
if (newValue && newValue.length > 0)
|
||||
await this.loadActivityDescriptors();
|
||||
}
|
||||
|
||||
async onPublishClicked() {
|
||||
await this.publishWorkflow();
|
||||
}
|
||||
@Watch("monacoLibPath")
|
||||
async monacoLibPathChangedHandler(newValue: string) {
|
||||
state.monacoLibPath = newValue;
|
||||
}
|
||||
|
||||
async onUnPublishClicked() {
|
||||
await this.unPublishWorkflow();
|
||||
}
|
||||
@Listen('workflow-changed')
|
||||
async workflowChangedHandler(event: CustomEvent<WorkflowModel>) {
|
||||
const workflowModel = event.detail;
|
||||
await this.saveWorkflowInternal(workflowModel);
|
||||
}
|
||||
|
||||
async onExportClicked() {
|
||||
await this.exportWorkflow();
|
||||
}
|
||||
async componentWillLoad() {
|
||||
await this.serverUrlChangedHandler(this.serverUrl);
|
||||
await this.workflowDefinitionIdChangedHandler(this.workflowDefinitionId);
|
||||
await this.monacoLibPathChangedHandler(this.monacoLibPath);
|
||||
}
|
||||
|
||||
async onImportClicked(file: File) {
|
||||
await this.importWorkflow(file);
|
||||
}
|
||||
connectedCallback() {
|
||||
eventBus.on(EventTypes.UpdateWorkflowSettings, this.onUpdateWorkflowSettings);
|
||||
}
|
||||
|
||||
render() {
|
||||
const tunnelState: WorkflowEditorState = {
|
||||
serverUrl: this.serverUrl,
|
||||
workflowDefinitionId: this.workflowDefinition.definitionId
|
||||
};
|
||||
disconnectedCallback() {
|
||||
eventBus.detach(EventTypes.UpdateWorkflowSettings, this.onUpdateWorkflowSettings);
|
||||
}
|
||||
|
||||
return (
|
||||
<Host class="flex flex-col w-full" ref={el => this.el = el}>
|
||||
<Tunnel.Provider state={tunnelState}>
|
||||
{this.renderCanvas()}
|
||||
{this.renderActivityPicker()}
|
||||
{this.renderActivityEditor()}
|
||||
</Tunnel.Provider>
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
componentDidLoad() {
|
||||
if (!this.designer) {
|
||||
this.designer = this.el.querySelector("elsa-designer-tree") as HTMLElsaDesignerTreeElement;
|
||||
this.designer.model = this.workflowModel;
|
||||
}
|
||||
}
|
||||
|
||||
renderCanvas() {
|
||||
return (
|
||||
<div class="flex-1 flex relative">
|
||||
<elsa-designer-tree model={this.workflowModel} class="flex-1" ref={el => this.designer = el}/>
|
||||
{this.renderWorkflowSettingsButton()}
|
||||
<elsa-workflow-settings-modal workflowDefinition={this.workflowDefinition}/>
|
||||
<elsa-workflow-definition-editor-notifications/>
|
||||
<div class="fixed bottom-10 right-12">
|
||||
<div class="flex items-center space-x-4">
|
||||
{this.renderSavingIndicator()}
|
||||
{this.renderNetworkError()}
|
||||
{this.renderPublishButton()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
async loadActivityDescriptors() {
|
||||
const client = createElsaClient(this.serverUrl);
|
||||
state.activityDescriptors = await client.activitiesApi.list();
|
||||
}
|
||||
|
||||
renderActivityPicker() {
|
||||
return <elsa-activity-picker-modal/>;
|
||||
}
|
||||
updateWorkflowDefinition(value: WorkflowDefinition) {
|
||||
this.workflowDefinition = value;
|
||||
this.workflowModel = this.mapWorkflowModel(value);
|
||||
}
|
||||
|
||||
renderActivityEditor() {
|
||||
return <elsa-activity-editor-modal/>;
|
||||
}
|
||||
async publishWorkflow() {
|
||||
this.publishing = true;
|
||||
await this.saveWorkflow(true);
|
||||
this.publishing = false;
|
||||
eventBus.emit(EventTypes.WorkflowPublished, this, this.workflowDefinition);
|
||||
}
|
||||
|
||||
renderWorkflowSettingsButton() {
|
||||
return (
|
||||
<button onClick={() => this.onShowWorkflowSettingsClick()} type="button"
|
||||
class="workflow-settings-button fixed top-20 right-12 inline-flex items-center p-2 rounded-full border border-transparent bg-white shadow text-gray-400 hover:text-blue-500 focus:text-blue-500 hover:ring-2 hover:ring-offset-2 hover:ring-blue-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" class="h-8 w-8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
||||
</svg>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
async unPublishWorkflow() {
|
||||
this.unPublishing = true;
|
||||
await this.unpublishWorkflow();
|
||||
this.unPublishing = false;
|
||||
eventBus.emit(EventTypes.WorkflowRetracted, this, this.workflowDefinition);
|
||||
}
|
||||
|
||||
renderWorkflowSettingsModal() {
|
||||
return;
|
||||
}
|
||||
async saveWorkflow(publish?: boolean) {
|
||||
await this.saveWorkflowInternal(null, publish);
|
||||
}
|
||||
|
||||
renderSavingIndicator() {
|
||||
async saveWorkflowInternal(workflowModel?: WorkflowModel, publish?: boolean) {
|
||||
if (!this.serverUrl || this.serverUrl.length == 0)
|
||||
return;
|
||||
|
||||
if (this.publishing)
|
||||
return undefined;
|
||||
workflowModel = workflowModel || this.workflowModel;
|
||||
|
||||
const message =
|
||||
this.unPublishing ? 'Unpublishing...' : this.unPublished ? 'Unpublished'
|
||||
: this.saving ? 'Saving...' : this.saved ? 'Saved'
|
||||
: this.importing ? 'Importing...' : this.imported ? 'Imported'
|
||||
: null;
|
||||
const client = createElsaClient(this.serverUrl);
|
||||
let workflowDefinition = this.workflowDefinition;
|
||||
|
||||
if (!message)
|
||||
return undefined;
|
||||
const request: SaveWorkflowDefinitionRequest = {
|
||||
workflowDefinitionId: workflowDefinition.definitionId || this.workflowDefinitionId,
|
||||
contextOptions: workflowDefinition.contextOptions,
|
||||
deleteCompletedInstances: workflowDefinition.deleteCompletedInstances,
|
||||
description: workflowDefinition.description,
|
||||
displayName: workflowDefinition.displayName,
|
||||
isSingleton: workflowDefinition.isSingleton,
|
||||
name: workflowDefinition.name,
|
||||
persistenceBehavior: workflowDefinition.persistenceBehavior,
|
||||
publish: publish || false,
|
||||
variables: workflowDefinition.variables,
|
||||
activities: workflowModel.activities.map<ActivityDefinition>(x => ({
|
||||
activityId: x.activityId,
|
||||
type: x.type,
|
||||
name: x.name,
|
||||
displayName: x.displayName,
|
||||
description: x.description,
|
||||
persistWorkflow: x.persistWorkflow,
|
||||
loadWorkflowContext: x.loadWorkflowContext,
|
||||
saveWorkflowContext: x.saveWorkflowContext,
|
||||
persistOutput: x.persistOutput,
|
||||
properties: x.properties
|
||||
})),
|
||||
connections: workflowModel.connections.map<ConnectionDefinition>(x => ({
|
||||
sourceActivityId: x.sourceId,
|
||||
targetActivityId: x.targetId,
|
||||
outcome: x.outcome
|
||||
})),
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<span class="text-gray-400 text-sm">{message}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
this.saving = !publish;
|
||||
this.publishing = publish;
|
||||
|
||||
renderNetworkError() {
|
||||
if (!this.networkError)
|
||||
return undefined;
|
||||
try {
|
||||
workflowDefinition = await client.workflowDefinitionsApi.save(request);
|
||||
this.workflowDefinition = workflowDefinition;
|
||||
this.workflowModel = this.mapWorkflowModel(workflowDefinition);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<span class="text-rose-400 text-sm">An error occurred: {this.networkError}</span>
|
||||
</div>);
|
||||
}
|
||||
this.saving = false;
|
||||
this.saved = !publish;
|
||||
this.publishing = false;
|
||||
setTimeout(() => this.saved = false, 500);
|
||||
this.workflowSaved.emit(workflowDefinition);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.saving = false;
|
||||
this.saved = false;
|
||||
this.networkError = e.message;
|
||||
setTimeout(() => this.networkError = null, 10000);
|
||||
}
|
||||
}
|
||||
|
||||
renderPublishButton() {
|
||||
return <elsa-workflow-publish-button
|
||||
publishing={this.publishing}
|
||||
workflowDefinition={this.workflowDefinition}
|
||||
onPublishClicked={() => this.onPublishClicked()}
|
||||
onUnPublishClicked={() => this.onUnPublishClicked()}
|
||||
onExportClicked={() => this.onExportClicked()}
|
||||
onImportClicked={e => this.onImportClicked(e.detail)}
|
||||
/>;
|
||||
}
|
||||
async unpublishWorkflow() {
|
||||
const client = createElsaClient(this.serverUrl);
|
||||
const workflowDefinitionId = this.workflowDefinition.definitionId;
|
||||
this.unPublishing = true;
|
||||
|
||||
private createWorkflowDefinition(): WorkflowDefinition {
|
||||
return {
|
||||
definitionId: this.workflowDefinitionId,
|
||||
version: 1,
|
||||
activities: [],
|
||||
connections: [],
|
||||
persistenceBehavior: WorkflowPersistenceBehavior.WorkflowBurst,
|
||||
};
|
||||
}
|
||||
try {
|
||||
this.workflowDefinition = await client.workflowDefinitionsApi.retract(workflowDefinitionId);
|
||||
this.unPublishing = false;
|
||||
this.unPublished = true
|
||||
setTimeout(() => this.unPublished = false, 500);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.unPublishing = false;
|
||||
this.unPublished = false;
|
||||
this.networkError = e.message;
|
||||
setTimeout(() => this.networkError = null, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
mapWorkflowModel(workflowDefinition: WorkflowDefinition): WorkflowModel {
|
||||
return {
|
||||
activities: workflowDefinition.activities.map(this.mapActivityModel),
|
||||
connections: workflowDefinition.connections.map(this.mapConnectionModel),
|
||||
persistenceBehavior: workflowDefinition.persistenceBehavior,
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
mapActivityModel(source: ActivityDefinition): ActivityModel {
|
||||
const descriptors: Array<ActivityDescriptor> = state.activityDescriptors;
|
||||
const descriptor = descriptors.find(x => x.type == source.type);
|
||||
|
||||
return {
|
||||
activityId: source.activityId,
|
||||
description: source.description,
|
||||
displayName: source.displayName,
|
||||
name: source.name,
|
||||
type: source.type,
|
||||
properties: source.properties,
|
||||
outcomes: [...descriptor.outcomes],
|
||||
persistOutput: source.persistOutput,
|
||||
persistWorkflow: source.persistWorkflow,
|
||||
saveWorkflowContext: source.saveWorkflowContext,
|
||||
loadWorkflowContext: source.loadWorkflowContext
|
||||
}
|
||||
}
|
||||
|
||||
mapConnectionModel(source: ConnectionDefinition): ConnectionModel {
|
||||
return {
|
||||
sourceId: source.sourceActivityId,
|
||||
targetId: source.targetActivityId,
|
||||
outcome: source.outcome
|
||||
}
|
||||
}
|
||||
|
||||
onShowWorkflowSettingsClick() {
|
||||
eventBus.emit(EventTypes.ShowWorkflowSettings);
|
||||
}
|
||||
|
||||
private onUpdateWorkflowSettings = async (workflowDefinition: WorkflowDefinition) => {
|
||||
this.updateWorkflowDefinition(workflowDefinition);
|
||||
await this.saveWorkflowInternal(this.workflowModel);
|
||||
}
|
||||
|
||||
async onPublishClicked() {
|
||||
await this.publishWorkflow();
|
||||
}
|
||||
|
||||
async onUnPublishClicked() {
|
||||
await this.unPublishWorkflow();
|
||||
}
|
||||
|
||||
async onExportClicked() {
|
||||
await this.exportWorkflow();
|
||||
}
|
||||
|
||||
async onImportClicked(file: File) {
|
||||
await this.importWorkflow(file);
|
||||
}
|
||||
|
||||
render() {
|
||||
const tunnelState: WorkflowEditorState = {
|
||||
serverUrl: this.serverUrl,
|
||||
workflowDefinitionId: this.workflowDefinition.definitionId
|
||||
};
|
||||
|
||||
return (
|
||||
<Host class="flex flex-col w-full" ref={el => this.el = el}>
|
||||
<Tunnel.Provider state={tunnelState}>
|
||||
{this.renderCanvas()}
|
||||
{this.renderActivityPicker()}
|
||||
{this.renderActivityEditor()}
|
||||
</Tunnel.Provider>
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
|
||||
renderCanvas() {
|
||||
return (
|
||||
<div class="flex-1 flex relative">
|
||||
<elsa-designer-tree model={this.workflowModel} class="flex-1" ref={el => this.designer = el}/>
|
||||
{this.renderWorkflowSettingsButton()}
|
||||
<elsa-workflow-settings-modal workflowDefinition={this.workflowDefinition}/>
|
||||
<elsa-workflow-definition-editor-notifications/>
|
||||
<div class="fixed bottom-10 right-12">
|
||||
<div class="flex items-center space-x-4">
|
||||
{this.renderSavingIndicator()}
|
||||
{this.renderNetworkError()}
|
||||
{this.renderPublishButton()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderActivityPicker() {
|
||||
return <elsa-activity-picker-modal/>;
|
||||
}
|
||||
|
||||
renderActivityEditor() {
|
||||
return <elsa-activity-editor-modal/>;
|
||||
}
|
||||
|
||||
renderWorkflowSettingsButton() {
|
||||
return (
|
||||
<button onClick={() => this.onShowWorkflowSettingsClick()} type="button"
|
||||
class="workflow-settings-button fixed top-20 right-12 inline-flex items-center p-2 rounded-full border border-transparent bg-white shadow text-gray-400 hover:text-blue-500 focus:text-blue-500 hover:ring-2 hover:ring-offset-2 hover:ring-blue-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" class="h-8 w-8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
||||
</svg>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
renderWorkflowSettingsModal() {
|
||||
return;
|
||||
}
|
||||
|
||||
renderSavingIndicator() {
|
||||
|
||||
if (this.publishing)
|
||||
return undefined;
|
||||
|
||||
const message =
|
||||
this.unPublishing ? 'Unpublishing...' : this.unPublished ? 'Unpublished'
|
||||
: this.saving ? 'Saving...' : this.saved ? 'Saved'
|
||||
: this.importing ? 'Importing...' : this.imported ? 'Imported'
|
||||
: null;
|
||||
|
||||
if (!message)
|
||||
return undefined;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<span class="text-gray-400 text-sm">{message}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderNetworkError() {
|
||||
if (!this.networkError)
|
||||
return undefined;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<span class="text-rose-400 text-sm">An error occurred: {this.networkError}</span>
|
||||
</div>);
|
||||
}
|
||||
|
||||
renderPublishButton() {
|
||||
return <elsa-workflow-publish-button
|
||||
publishing={this.publishing}
|
||||
workflowDefinition={this.workflowDefinition}
|
||||
onPublishClicked={() => this.onPublishClicked()}
|
||||
onUnPublishClicked={() => this.onUnPublishClicked()}
|
||||
onExportClicked={() => this.onExportClicked()}
|
||||
onImportClicked={e => this.onImportClicked(e.detail)}
|
||||
/>;
|
||||
}
|
||||
|
||||
private static createWorkflowDefinition(): WorkflowDefinition {
|
||||
return {
|
||||
definitionId: null,
|
||||
version: 1,
|
||||
activities: [],
|
||||
connections: [],
|
||||
persistenceBehavior: WorkflowPersistenceBehavior.WorkflowBurst,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue