diff --git a/src/clients/Elsa.Client/Models/WorkflowInstance.cs b/src/clients/Elsa.Client/Models/WorkflowInstance.cs index 8a6890ee2..5edd00bc9 100644 --- a/src/clients/Elsa.Client/Models/WorkflowInstance.cs +++ b/src/clients/Elsa.Client/Models/WorkflowInstance.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Runtime.Serialization; using Elsa.Client.Comparers; +using Newtonsoft.Json.Linq; using NodaTime; namespace Elsa.Client.Models @@ -13,7 +14,6 @@ namespace Elsa.Client.Models public WorkflowInstance() { Variables = new Variables(); - Activities = new List(); ScheduledActivities = new SimpleStack(); Scopes = new SimpleStack(); } @@ -27,21 +27,24 @@ namespace Elsa.Client.Models [DataMember(Order = 7)] public string? Name { get; set; } [DataMember(Order = 8)] public Instant CreatedAt { get; set; } [DataMember(Order = 9)] public Instant? LastExecutedAt { get; set; } - [DataMember(Order = 10)] public Instant? LastBurstAt { get; set; } [DataMember(Order = 11)] public Instant? FinishedAt { get; set; } - [DataMember(Order = 12)] public Variables Variables { get; set; } - [DataMember(Order = 13)] public object? Output { get; set; } - [DataMember(Order = 14)] public ICollection Activities { get; set; } + [DataMember(Order = 12)] public Instant? CancelledAt { get; set; } + [DataMember(Order = 13)] public Instant? FaultedAt { get; set; } + [DataMember(Order = 14)] public Variables Variables { get; set; } + [DataMember(Order = 15)] public object? Output { get; set; } + [DataMember(Order = 16)] public IDictionary ActivityData { get; set; } = new Dictionary(); + [DataMember(Order = 17)] public IDictionary ActivityOutput { get; set; } = new Dictionary(); - [DataMember(Order = 15)] + [DataMember(Order = 18)] public HashSet BlockingActivities { get => _blockingActivities; set => _blockingActivities = new HashSet(value, BlockingActivityEqualityComparer.Instance); } - - [DataMember(Order = 16)] public WorkflowFault? Fault { get; set; } - [DataMember(Order = 17)] public SimpleStack ScheduledActivities { get; set; } - [DataMember(Order = 18)]public SimpleStack Scopes { get; set; } + + [DataMember(Order = 19)] public WorkflowFault? Fault { get; set; } + [DataMember(Order = 20)] public SimpleStack ScheduledActivities { get; set; } + [DataMember(Order = 21)] public SimpleStack Scopes { get; set; } + [DataMember(Order = 22)] public ScheduledActivity? CurrentActivity { get; set; } } } \ No newline at end of file diff --git a/src/designer/elsa-workflows-studio/src/components.d.ts b/src/designer/elsa-workflows-studio/src/components.d.ts index 122af9428..b5d3fd645 100644 --- a/src/designer/elsa-workflows-studio/src/components.d.ts +++ b/src/designer/elsa-workflows-studio/src/components.d.ts @@ -139,10 +139,18 @@ export namespace Components { "history": RouterHistory; "serverUrl": string; } + interface ElsaStudioWorkflowInstanceView { + "match": MatchResults; + "serverUrl": string; + } interface ElsaStudioWorkflowInstancesList { "history": RouterHistory; "serverUrl": string; } + interface ElsaStudioWorkflowInstancesView { + "match": MatchResults; + "serverUrl": string; + } interface ElsaStudioWorkflowRegistry { } interface ElsaSwitchCasesProperty { @@ -168,6 +176,11 @@ export namespace Components { "history"?: RouterHistory; "serverUrl": string; } + interface ElsaWorkflowInstanceViewerScreen { + "getServerUrl": () => Promise; + "serverUrl": string; + "workflowInstanceId": string; + } interface ElsaWorkflowInstancesListScreen { "history"?: RouterHistory; "serverUrl": string; @@ -325,12 +338,24 @@ declare global { prototype: HTMLElsaStudioWorkflowDefinitionsListElement; new (): HTMLElsaStudioWorkflowDefinitionsListElement; }; + interface HTMLElsaStudioWorkflowInstanceViewElement extends Components.ElsaStudioWorkflowInstanceView, HTMLStencilElement { + } + var HTMLElsaStudioWorkflowInstanceViewElement: { + prototype: HTMLElsaStudioWorkflowInstanceViewElement; + new (): HTMLElsaStudioWorkflowInstanceViewElement; + }; interface HTMLElsaStudioWorkflowInstancesListElement extends Components.ElsaStudioWorkflowInstancesList, HTMLStencilElement { } var HTMLElsaStudioWorkflowInstancesListElement: { prototype: HTMLElsaStudioWorkflowInstancesListElement; new (): HTMLElsaStudioWorkflowInstancesListElement; }; + interface HTMLElsaStudioWorkflowInstancesViewElement extends Components.ElsaStudioWorkflowInstancesView, HTMLStencilElement { + } + var HTMLElsaStudioWorkflowInstancesViewElement: { + prototype: HTMLElsaStudioWorkflowInstancesViewElement; + new (): HTMLElsaStudioWorkflowInstancesViewElement; + }; interface HTMLElsaStudioWorkflowRegistryElement extends Components.ElsaStudioWorkflowRegistry, HTMLStencilElement { } var HTMLElsaStudioWorkflowRegistryElement: { @@ -367,6 +392,12 @@ declare global { prototype: HTMLElsaWorkflowDefinitionsListScreenElement; new (): HTMLElsaWorkflowDefinitionsListScreenElement; }; + interface HTMLElsaWorkflowInstanceViewerScreenElement extends Components.ElsaWorkflowInstanceViewerScreen, HTMLStencilElement { + } + var HTMLElsaWorkflowInstanceViewerScreenElement: { + prototype: HTMLElsaWorkflowInstanceViewerScreenElement; + new (): HTMLElsaWorkflowInstanceViewerScreenElement; + }; interface HTMLElsaWorkflowInstancesListScreenElement extends Components.ElsaWorkflowInstancesListScreen, HTMLStencilElement { } var HTMLElsaWorkflowInstancesListScreenElement: { @@ -410,13 +441,16 @@ declare global { "elsa-studio-root": HTMLElsaStudioRootElement; "elsa-studio-workflow-definitions-edit": HTMLElsaStudioWorkflowDefinitionsEditElement; "elsa-studio-workflow-definitions-list": HTMLElsaStudioWorkflowDefinitionsListElement; + "elsa-studio-workflow-instance-view": HTMLElsaStudioWorkflowInstanceViewElement; "elsa-studio-workflow-instances-list": HTMLElsaStudioWorkflowInstancesListElement; + "elsa-studio-workflow-instances-view": HTMLElsaStudioWorkflowInstancesViewElement; "elsa-studio-workflow-registry": HTMLElsaStudioWorkflowRegistryElement; "elsa-switch-cases-property": HTMLElsaSwitchCasesPropertyElement; "elsa-toast-notification": HTMLElsaToastNotificationElement; "elsa-workflow-definition-editor-notifications": HTMLElsaWorkflowDefinitionEditorNotificationsElement; "elsa-workflow-definition-editor-screen": HTMLElsaWorkflowDefinitionEditorScreenElement; "elsa-workflow-definitions-list-screen": HTMLElsaWorkflowDefinitionsListScreenElement; + "elsa-workflow-instance-viewer-screen": HTMLElsaWorkflowInstanceViewerScreenElement; "elsa-workflow-instances-list-screen": HTMLElsaWorkflowInstancesListScreenElement; "elsa-workflow-publish-button": HTMLElsaWorkflowPublishButtonElement; "elsa-workflow-settings-modal": HTMLElsaWorkflowSettingsModalElement; @@ -551,10 +585,18 @@ declare namespace LocalJSX { "history"?: RouterHistory; "serverUrl"?: string; } + interface ElsaStudioWorkflowInstanceView { + "match"?: MatchResults; + "serverUrl"?: string; + } interface ElsaStudioWorkflowInstancesList { "history"?: RouterHistory; "serverUrl"?: string; } + interface ElsaStudioWorkflowInstancesView { + "match"?: MatchResults; + "serverUrl"?: string; + } interface ElsaStudioWorkflowRegistry { } interface ElsaSwitchCasesProperty { @@ -575,6 +617,10 @@ declare namespace LocalJSX { "history"?: RouterHistory; "serverUrl"?: string; } + interface ElsaWorkflowInstanceViewerScreen { + "serverUrl"?: string; + "workflowInstanceId"?: string; + } interface ElsaWorkflowInstancesListScreen { "history"?: RouterHistory; "serverUrl"?: string; @@ -615,13 +661,16 @@ declare namespace LocalJSX { "elsa-studio-root": ElsaStudioRoot; "elsa-studio-workflow-definitions-edit": ElsaStudioWorkflowDefinitionsEdit; "elsa-studio-workflow-definitions-list": ElsaStudioWorkflowDefinitionsList; + "elsa-studio-workflow-instance-view": ElsaStudioWorkflowInstanceView; "elsa-studio-workflow-instances-list": ElsaStudioWorkflowInstancesList; + "elsa-studio-workflow-instances-view": ElsaStudioWorkflowInstancesView; "elsa-studio-workflow-registry": ElsaStudioWorkflowRegistry; "elsa-switch-cases-property": ElsaSwitchCasesProperty; "elsa-toast-notification": ElsaToastNotification; "elsa-workflow-definition-editor-notifications": ElsaWorkflowDefinitionEditorNotifications; "elsa-workflow-definition-editor-screen": ElsaWorkflowDefinitionEditorScreen; "elsa-workflow-definitions-list-screen": ElsaWorkflowDefinitionsListScreen; + "elsa-workflow-instance-viewer-screen": ElsaWorkflowInstanceViewerScreen; "elsa-workflow-instances-list-screen": ElsaWorkflowInstancesListScreen; "elsa-workflow-publish-button": ElsaWorkflowPublishButton; "elsa-workflow-settings-modal": ElsaWorkflowSettingsModal; @@ -655,13 +704,16 @@ declare module "@stencil/core" { "elsa-studio-root": LocalJSX.ElsaStudioRoot & JSXBase.HTMLAttributes; "elsa-studio-workflow-definitions-edit": LocalJSX.ElsaStudioWorkflowDefinitionsEdit & JSXBase.HTMLAttributes; "elsa-studio-workflow-definitions-list": LocalJSX.ElsaStudioWorkflowDefinitionsList & JSXBase.HTMLAttributes; + "elsa-studio-workflow-instance-view": LocalJSX.ElsaStudioWorkflowInstanceView & JSXBase.HTMLAttributes; "elsa-studio-workflow-instances-list": LocalJSX.ElsaStudioWorkflowInstancesList & JSXBase.HTMLAttributes; + "elsa-studio-workflow-instances-view": LocalJSX.ElsaStudioWorkflowInstancesView & JSXBase.HTMLAttributes; "elsa-studio-workflow-registry": LocalJSX.ElsaStudioWorkflowRegistry & JSXBase.HTMLAttributes; "elsa-switch-cases-property": LocalJSX.ElsaSwitchCasesProperty & JSXBase.HTMLAttributes; "elsa-toast-notification": LocalJSX.ElsaToastNotification & JSXBase.HTMLAttributes; "elsa-workflow-definition-editor-notifications": LocalJSX.ElsaWorkflowDefinitionEditorNotifications & JSXBase.HTMLAttributes; "elsa-workflow-definition-editor-screen": LocalJSX.ElsaWorkflowDefinitionEditorScreen & JSXBase.HTMLAttributes; "elsa-workflow-definitions-list-screen": LocalJSX.ElsaWorkflowDefinitionsListScreen & JSXBase.HTMLAttributes; + "elsa-workflow-instance-viewer-screen": LocalJSX.ElsaWorkflowInstanceViewerScreen & JSXBase.HTMLAttributes; "elsa-workflow-instances-list-screen": LocalJSX.ElsaWorkflowInstancesListScreen & JSXBase.HTMLAttributes; "elsa-workflow-publish-button": LocalJSX.ElsaWorkflowPublishButton & JSXBase.HTMLAttributes; "elsa-workflow-settings-modal": LocalJSX.ElsaWorkflowSettingsModal & JSXBase.HTMLAttributes; diff --git a/src/designer/elsa-workflows-studio/src/components/dashboard/pages/elsa-studio-root/elsa-studio-root.tsx b/src/designer/elsa-workflows-studio/src/components/dashboard/pages/elsa-studio-root/elsa-studio-root.tsx index e14dbe512..61ae2ad8b 100644 --- a/src/designer/elsa-workflows-studio/src/components/dashboard/pages/elsa-studio-root/elsa-studio-root.tsx +++ b/src/designer/elsa-workflows-studio/src/components/dashboard/pages/elsa-studio-root/elsa-studio-root.tsx @@ -49,8 +49,9 @@ export class ElsaStudioRoot { } exact={true}/> - + + {/**/} diff --git a/src/designer/elsa-workflows-studio/src/components/dashboard/pages/elsa-studio-workflow-definitions-edit/elsa-studio-workflow-definitions-edit.tsx b/src/designer/elsa-workflows-studio/src/components/dashboard/pages/elsa-studio-workflow-definitions-edit/elsa-studio-workflow-definitions-edit.tsx index 54179ff68..a559a6b9d 100644 --- a/src/designer/elsa-workflows-studio/src/components/dashboard/pages/elsa-studio-workflow-definitions-edit/elsa-studio-workflow-definitions-edit.tsx +++ b/src/designer/elsa-workflows-studio/src/components/dashboard/pages/elsa-studio-workflow-definitions-edit/elsa-studio-workflow-definitions-edit.tsx @@ -14,7 +14,7 @@ export class ElsaStudioWorkflowDefinitionsEdit { componentWillLoad() { let id = this.match.params.id; - if (id == 'new') + if (!!id && id.toLowerCase() == 'new') id = null; this.id = id; diff --git a/src/designer/elsa-workflows-studio/src/components/dashboard/pages/elsa-studio-workflow-instances-view/elsa-studio-workflow-instances-view.tsx b/src/designer/elsa-workflows-studio/src/components/dashboard/pages/elsa-studio-workflow-instances-view/elsa-studio-workflow-instances-view.tsx new file mode 100644 index 000000000..4c815c485 --- /dev/null +++ b/src/designer/elsa-workflows-studio/src/components/dashboard/pages/elsa-studio-workflow-instances-view/elsa-studio-workflow-instances-view.tsx @@ -0,0 +1,25 @@ +import {Component, Prop, h} from '@stencil/core'; +import {MatchResults} from '@stencil/router'; + +@Component({ + tag: 'elsa-studio-workflow-instances-view', + shadow: false, +}) +export class ElsaStudioWorkflowInstancesView { + @Prop() match: MatchResults; + @Prop() serverUrl: string; + + id?: string; + + componentWillLoad() { + this.id = this.match.params.id; + } + + render() { + const id = this.id; + + return
+ +
; + } +} diff --git a/src/designer/elsa-workflows-studio/src/components/screens/workflow-instance-viewer/elsa-workflow-instance-viewer-screen/elsa-workflow-instance-viewer-screen.tsx b/src/designer/elsa-workflows-studio/src/components/screens/workflow-instance-viewer/elsa-workflow-instance-viewer-screen/elsa-workflow-instance-viewer-screen.tsx new file mode 100644 index 000000000..b65184743 --- /dev/null +++ b/src/designer/elsa-workflows-studio/src/components/screens/workflow-instance-viewer/elsa-workflow-instance-viewer-screen/elsa-workflow-instance-viewer-screen.tsx @@ -0,0 +1,175 @@ +import {Component, h, Host, Method, Prop, State, Watch} from '@stencil/core'; +import {eventBus} from '../../../../services/event-bus'; +import * as collection from 'lodash/collection'; +import { + ActivityBlueprint, ActivityDefinitionProperty, + ActivityDescriptor, + ActivityModel, Connection, + ConnectionModel, + EventTypes, SyntaxNames, + WorkflowBlueprint, + WorkflowInstance, + WorkflowModel, + WorkflowPersistenceBehavior, + WorkflowStatus +} from "../../../../models"; +import {createElsaClient} from "../../../../services/elsa-client"; +import {pluginManager} from '../../../../services/plugin-manager'; +import state from '../../../../utils/store'; + +@Component({ + tag: 'elsa-workflow-instance-viewer-screen', + shadow: false, +}) +export class ElsaWorkflowInstanceViewerScreen { + + constructor() { + pluginManager.initialize(); + } + + @Prop() workflowInstanceId: string; + @Prop({attribute: 'server-url', reflect: true}) serverUrl: string; + @State() workflowInstance: WorkflowInstance; + @State() workflowBlueprint: WorkflowBlueprint; + @State() workflowModel: WorkflowModel; + el: HTMLElement; + designer: HTMLElsaDesignerTreeElement; + + @Method() + async getServerUrl(): Promise { + return this.serverUrl; + } + + @Watch('workflowInstanceId') + async workflowInstanceIdChangedHandler(newValue: string) { + const workflowInstanceId = newValue; + let workflowInstance: WorkflowInstance = { + id: null, + definitionId: null, + version: null, + workflowStatus: WorkflowStatus.Idle, + variables: {data: {}}, + blockingActivities: [], + scheduledActivities: [], + scopes: [], + currentActivity: {activityId: ''} + }; + + let workflowBlueprint: WorkflowBlueprint = { + id: null, + version: 1, + activities: [], + connections: [], + persistenceBehavior: WorkflowPersistenceBehavior.WorkflowBurst, + customAttributes: {data: {}}, + persistWorkflow: false, + isLatest: false, + isPublished: false, + loadWorkflowContext: false, + isSingleton: false, + persistOutput: false, + saveWorkflowContext: false, + variables: {data: {}}, + type: null, + properties: {data: {}} + }; + + const client = createElsaClient(this.serverUrl); + + if (workflowInstanceId && workflowInstanceId.length > 0) { + try { + workflowInstance = await client.workflowInstancesApi.get(workflowInstanceId); + workflowBlueprint = await client.workflowRegistryApi.get(workflowInstance.definitionId, {version: workflowInstance.version}); + } catch { + console.warn(`The specified workflow definition does not exist. Creating a new one.`); + } + } + + this.updateModels(workflowInstance, workflowBlueprint); + } + + @Watch("serverUrl") + async serverUrlChangedHandler(newValue: string) { + if (newValue && newValue.length > 0) + await this.loadActivityDescriptors(); + } + + async componentWillLoad() { + await this.serverUrlChangedHandler(this.serverUrl); + await this.workflowInstanceIdChangedHandler(this.workflowInstanceId); + } + + componentDidLoad() { + if (!this.designer) { + this.designer = this.el.querySelector("elsa-designer-tree") as HTMLElsaDesignerTreeElement; + this.designer.model = this.workflowModel; + } + } + + async loadActivityDescriptors() { + const client = createElsaClient(this.serverUrl); + state.activityDescriptors = await client.activitiesApi.list(); + } + + updateModels(workflowInstance: WorkflowInstance, workflowBlueprint: WorkflowBlueprint) { + this.workflowInstance = workflowInstance; + this.workflowBlueprint = workflowBlueprint; + this.workflowModel = this.mapWorkflowModel(workflowBlueprint); + } + + mapWorkflowModel(workflowBlueprint: WorkflowBlueprint): WorkflowModel { + return { + activities: workflowBlueprint.activities.map(this.mapActivityModel), + connections: workflowBlueprint.connections.map(this.mapConnectionModel), + persistenceBehavior: workflowBlueprint.persistenceBehavior, + }; + } + + mapActivityModel(source: ActivityBlueprint): ActivityModel { + const descriptors: Array = state.activityDescriptors; + const descriptor = descriptors.find(x => x.type == source.type); + const properties: Array = collection.map(source.properties.data, (v, k) => ({name: k, expressions: {'Literal': v}, syntax: SyntaxNames.Literal})); + + return { + activityId: source.id, + description: source.description, + displayName: source.displayName, + name: source.name, + type: source.type, + properties: properties, + outcomes: [...descriptor.outcomes], + persistOutput: source.persistOutput, + persistWorkflow: source.persistWorkflow, + saveWorkflowContext: source.saveWorkflowContext, + loadWorkflowContext: source.loadWorkflowContext + } + } + + mapConnectionModel(connection: Connection): ConnectionModel { + return { + sourceId: connection.sourceActivityId, + targetId: connection.targetActivityId, + outcome: connection.outcome + } + } + + onShowWorkflowSettingsClick() { + eventBus.emit(EventTypes.ShowWorkflowSettings); + } + + render() { + return ( + this.el = el}> + {this.renderCanvas()} + + ); + } + + renderCanvas() { + return ( +
+ this.designer = el}/> +
+ ); + } +} diff --git a/src/designer/elsa-workflows-studio/src/components/screens/workflow-instances-list/elsa-workflow-instances-list-screen/elsa-workflow-instances-list-screen.tsx b/src/designer/elsa-workflows-studio/src/components/screens/workflow-instances-list/elsa-workflow-instances-list-screen/elsa-workflow-instances-list-screen.tsx index e3948b69a..1d0dd211e 100644 --- a/src/designer/elsa-workflows-studio/src/components/screens/workflow-instances-list/elsa-workflow-instances-list-screen/elsa-workflow-instances-list-screen.tsx +++ b/src/designer/elsa-workflows-studio/src/components/screens/workflow-instances-list/elsa-workflow-instances-list-screen/elsa-workflow-instances-list-screen.tsx @@ -108,7 +108,6 @@ export class ElsaWorkflowInstancesListScreen { } async routeChanged(e: LocationSegments) { - debugger; if (e.pathname.toLowerCase().indexOf('workflow-instances') < 0) return; @@ -286,7 +285,7 @@ export class ElsaWorkflowInstancesListScreen { const workflowBlueprint = workflowBlueprints.find(x => x.id == workflowInstance.definitionId && x.version == workflowInstance.version) ?? {displayName: '(Workflow definition not found)'}; const displayName = workflowBlueprint.displayName; const statusColor = this.getStatusColor(workflowInstance.workflowStatus); - const viewUrl = `/workflow-instances/${workflowInstance.id}/viewer`; + const viewUrl = `/workflow-instances/${workflowInstance.id}`; const instanceName = !workflowInstance.name ? '' : workflowInstance.name; const isSelected = this.selectedWorkflowInstanceIds.findIndex(x => x === workflowInstance.id) >= 0; //var displayContext = WorkflowInstanceDisplayContexts[workflowInstance]; @@ -297,7 +296,7 @@ export class ElsaWorkflowInstancesListScreen { class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300 rounded"/> - {workflowInstance.id} + {workflowInstance.id} @@ -308,7 +307,7 @@ export class ElsaWorkflowInstancesListScreen { {workflowInstance.version} - {instanceName} + {instanceName}
diff --git a/src/designer/elsa-workflows-studio/src/models/domain.ts b/src/designer/elsa-workflows-studio/src/models/domain.ts index 7e038015d..fe77bdbfc 100644 --- a/src/designer/elsa-workflows-studio/src/models/domain.ts +++ b/src/designer/elsa-workflows-studio/src/models/domain.ts @@ -34,6 +34,43 @@ export interface WorkflowDefinitionSummary { isLatest?: boolean; } +export interface ActivityBlueprint { + id: string; + name?: string; + displayName?: string; + description?: string; + type: string; + persistWorkflow: boolean; + loadWorkflowContext: boolean; + saveWorkflowContext: boolean; + persistOutput: boolean; + source?: string; + properties: Variables; +} + +export interface Connection { + sourceActivityId: string; + targetActivityId: string; + outcome: string; +} + +export interface CompositeActivityBlueprint extends ActivityBlueprint { + activities: Array; + connections: Array; +} + +export interface WorkflowBlueprint extends CompositeActivityBlueprint { + version: number; + tenantId?: string; + isSingleton: boolean; + isPublished: boolean; + isLatest: boolean; + contextOptions?: WorkflowContextOptions; + variables: Variables; + customAttributes: Variables; + persistenceBehavior: WorkflowPersistenceBehavior; +} + export interface WorkflowBlueprintSummary { id: string; name?: string; @@ -46,6 +83,31 @@ export interface WorkflowBlueprintSummary { isLatest: boolean; } +export interface WorkflowInstance { + id: string; + definitionId: string; + tenantId?: string; + version: number; + workflowStatus: WorkflowStatus; + correlationId?: string; + contextType?: string; + contextId?: string; + name?: string; + createdAt?: Date + lastExecutedAt?: Date; + finishedAt?: Date; + faultedAt?: Date; + variables: Variables; + output?: any; + activityData?: Map; + activityOutput?: Map; + blockingActivities: Array; + fault?: WorkflowFault; + scheduledActivities: Array; + scopes: Array; + currentActivity: ScheduledActivity; +} + export interface WorkflowInstanceSummary { id: string; definitionId: string; @@ -83,11 +145,11 @@ export interface ConnectionDefinition { } export interface ConnectionDefinitionMapped { - sourceId: string; - sourceActivityId: string; - targetId: string; - targetActivityId: string; - outcome: string; + sourceId: string; + sourceActivityId: string; + targetId: string; + targetActivityId: string; + outcome: string; } export interface ActivityDefinitionProperty { @@ -96,6 +158,22 @@ export interface ActivityDefinitionProperty { expressions: Map; } +interface BlockingActivity { + activityId: string; + activityType: string; + tag?: string; +} + +interface ScheduledActivity { + activityId: string; + input?: any; +} + +interface ActivityScope { + activityId: string; + variables: Variables; +} + export interface Variables { data: Map; } @@ -141,23 +219,20 @@ export enum OrderBy { Finished = 'Finished' } -export const getVersionOptionsString = (versionOptions?: VersionOptions) => { +interface SimpleException { + type: string; + message: string; + stackTrace: string; + innerException?: SimpleException; +} - if (!versionOptions) - return ''; - - return versionOptions.allVersions - ? 'AllVersions' - : versionOptions.isDraft - ? 'Draft' - : versionOptions.isLatest - ? 'Latest' - : versionOptions.isPublished - ? 'Published' - : versionOptions.isLatestOrPublished - ? 'LatestOrPublished' - : versionOptions.version.toString(); -}; +interface WorkflowFault { + exception?: SimpleException; + message: string; + faultedActivityId?: string; + activityInput?: any; + resuming: boolean; +} export interface ActivityDescriptor { type: string; @@ -203,3 +278,21 @@ export class SyntaxNames { static Variable = 'Variable'; static Output = 'Output'; } + +export const getVersionOptionsString = (versionOptions?: VersionOptions) => { + + if (!versionOptions) + return ''; + + return versionOptions.allVersions + ? 'AllVersions' + : versionOptions.isDraft + ? 'Draft' + : versionOptions.isLatest + ? 'Latest' + : versionOptions.isPublished + ? 'Published' + : versionOptions.isLatestOrPublished + ? 'LatestOrPublished' + : versionOptions.version.toString(); +}; \ No newline at end of file diff --git a/src/designer/elsa-workflows-studio/src/services/elsa-client.ts b/src/designer/elsa-workflows-studio/src/services/elsa-client.ts index 89ae0f561..8f1da4935 100644 --- a/src/designer/elsa-workflows-studio/src/services/elsa-client.ts +++ b/src/designer/elsa-workflows-studio/src/services/elsa-client.ts @@ -1,203 +1,218 @@ import axios, {AxiosRequestConfig} from "axios"; import * as collection from 'lodash/collection'; import { - ActivityDefinition, - ActivityDescriptor, - ConnectionDefinition, - getVersionOptionsString, OrderBy, - PagedList, - Variables, - VersionOptions, WorkflowBlueprintSummary, - WorkflowContextOptions, - WorkflowDefinition, - WorkflowDefinitionSummary, WorkflowInstanceSummary, - WorkflowPersistenceBehavior, WorkflowStatus + ActivityDefinition, + ActivityDescriptor, + ConnectionDefinition, + getVersionOptionsString, OrderBy, + PagedList, + Variables, + VersionOptions, WorkflowBlueprint, WorkflowBlueprintSummary, + WorkflowContextOptions, + WorkflowDefinition, + WorkflowDefinitionSummary, WorkflowInstance, WorkflowInstanceSummary, + WorkflowPersistenceBehavior, WorkflowStatus } from "../models"; export const createElsaClient = function (serverUrl: string): ElsaClient { - const config: AxiosRequestConfig = { - baseURL: serverUrl - }; + const config: AxiosRequestConfig = { + baseURL: serverUrl + }; - const httpClient = axios.create(config); + const httpClient = axios.create(config); - return { - activitiesApi: { - list: async () => { - const response = await httpClient.get>('v1/activities'); - return response.data; - } - }, - workflowDefinitionsApi: { - list: async (page?: number, pageSize?: number, versionOptions?: VersionOptions) => { - const versionOptionsString = getVersionOptionsString(versionOptions); - const response = await httpClient.get>(`v1/workflow-definitions?version=${versionOptionsString}`); - return response.data; - }, - getByDefinitionAndVersion: async (definitionId: string, versionOptions: VersionOptions) => { - const versionOptionsString = getVersionOptionsString(versionOptions); - const response = await httpClient.get(`v1/workflow-definitions/${definitionId}/${versionOptionsString}`); - return response.data; - }, - save: async request => { - const response = await httpClient.post('v1/workflow-definitions', request); - return response.data; - }, - delete: async definitionId => { - await httpClient.delete(`v1/workflow-definitions/${definitionId}`); - }, - retract: async workflowDefinitionId => { - const response = await httpClient.post(`v1/workflow-definitions/${workflowDefinitionId}/retract`); - return response.data; - }, - export: async (workflowDefinitionId, versionOptions): Promise => { - const versionOptionsString = getVersionOptionsString(versionOptions); - const response = await httpClient.post(`v1/workflow-definitions/${workflowDefinitionId}/${versionOptionsString}/export`, null, { - responseType: 'blob' - }); + return { + activitiesApi: { + list: async () => { + const response = await httpClient.get>('v1/activities'); + return response.data; + } + }, + workflowDefinitionsApi: { + list: async (page?: number, pageSize?: number, versionOptions?: VersionOptions) => { + const versionOptionsString = getVersionOptionsString(versionOptions); + const response = await httpClient.get>(`v1/workflow-definitions?version=${versionOptionsString}`); + return response.data; + }, + getByDefinitionAndVersion: async (definitionId: string, versionOptions: VersionOptions) => { + const versionOptionsString = getVersionOptionsString(versionOptions); + const response = await httpClient.get(`v1/workflow-definitions/${definitionId}/${versionOptionsString}`); + return response.data; + }, + save: async request => { + const response = await httpClient.post('v1/workflow-definitions', request); + return response.data; + }, + delete: async definitionId => { + await httpClient.delete(`v1/workflow-definitions/${definitionId}`); + }, + retract: async workflowDefinitionId => { + const response = await httpClient.post(`v1/workflow-definitions/${workflowDefinitionId}/retract`); + return response.data; + }, + export: async (workflowDefinitionId, versionOptions): Promise => { + const versionOptionsString = getVersionOptionsString(versionOptions); + const response = await httpClient.post(`v1/workflow-definitions/${workflowDefinitionId}/${versionOptionsString}/export`, null, { + responseType: 'blob' + }); - const contentDispositionHeader = response.headers["content-disposition"]; // Only available if the Elsa Server exposes the "Content-Disposition" header. - const fileName = contentDispositionHeader ? contentDispositionHeader.split(";")[1].split("=")[1] : `workflow-definition-${workflowDefinitionId}.json`; - const data = response.data; + const contentDispositionHeader = response.headers["content-disposition"]; // Only available if the Elsa Server exposes the "Content-Disposition" header. + const fileName = contentDispositionHeader ? contentDispositionHeader.split(";")[1].split("=")[1] : `workflow-definition-${workflowDefinitionId}.json`; + const data = response.data; - return { - fileName: fileName, - data: data - }; - }, - import: async (workflowDefinitionId, file: File): Promise => { - const formData = new FormData(); - formData.append("file", file); - const response = await httpClient.post(`v1/workflow-definitions/${workflowDefinitionId}/import`, formData, { - headers: { - 'Content-Type': 'multipart/form-data' - } - }); - return response.data; - } - }, - workflowRegistryApi: { - list: async (page?: number, pageSize?: number, versionOptions?: VersionOptions): Promise> => { - const versionOptionsString = getVersionOptionsString(versionOptions); - const response = await httpClient.get>(`v1/workflow-registry?version=${versionOptionsString}`); - return response.data; - } - }, - workflowInstancesApi: { - list: async (page?: number, pageSize?: number, workflowDefinitionId?: string, workflowStatus?: WorkflowStatus, orderBy?: OrderBy, searchTerm?: string): Promise> => { - const queryString = {}; + return { + fileName: fileName, + data: data + }; + }, + import: async (workflowDefinitionId, file: File): Promise => { + const formData = new FormData(); + formData.append("file", file); + const response = await httpClient.post(`v1/workflow-definitions/${workflowDefinitionId}/import`, formData, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }); + return response.data; + } + }, + workflowRegistryApi: { + list: async (page?: number, pageSize?: number, versionOptions?: VersionOptions): Promise> => { + const versionOptionsString = getVersionOptionsString(versionOptions); + const response = await httpClient.get>(`v1/workflow-registry?version=${versionOptionsString}`); + return response.data; + }, - if (!!workflowDefinitionId) - queryString['workflow'] = workflowDefinitionId; + get: async (id: string, versionOptions: VersionOptions) => { + const versionOptionsString = getVersionOptionsString(versionOptions); + const response = await httpClient.get(`v1/workflow-registry/${id}/${versionOptionsString}`); + return response.data; + } + }, + workflowInstancesApi: { + list: async (page?: number, pageSize?: number, workflowDefinitionId?: string, workflowStatus?: WorkflowStatus, orderBy?: OrderBy, searchTerm?: string): Promise> => { + const queryString = {}; - if (workflowStatus != null) - queryString['status'] = workflowStatus; + if (!!workflowDefinitionId) + queryString['workflow'] = workflowDefinitionId; - if (!!orderBy) - queryString['orderBy'] = orderBy; + if (workflowStatus != null) + queryString['status'] = workflowStatus; - if (!!searchTerm) - queryString['searchTerm'] = searchTerm; + if (!!orderBy) + queryString['orderBy'] = orderBy; - if (!!page) - queryString['page'] = page; + if (!!searchTerm) + queryString['searchTerm'] = searchTerm; - if (!!pageSize) - queryString['pageSize'] = pageSize; + if (!!page) + queryString['page'] = page; - const queryStringItems = collection.map(queryString, (v, k) => `${k}=${v}`); - const queryStringText = queryStringItems.length > 0 ? `?${queryStringItems.join('&')}` : ''; - const response = await httpClient.get>(`v1/workflow-instances${queryStringText}`); - return response.data; - }, - delete: async id => { - await httpClient.delete(`v1/workflow-instances/${id}`); - }, - bulkDelete: async request => { - const response = await httpClient.delete(`v1/workflow-instances/bulk`, { - data: request - }); - return response.data; - } - }, - scriptingApi: { - getJavaScriptTypeDefinitions: async (workflowDefinitionId: string, context?: string): Promise => { - context = context || ''; - const response = await httpClient.get(`v1/scripting/javascript/type-definitions/${workflowDefinitionId}?t=${new Date().getTime()}&context=${context}`); - return response.data; - } - } + if (!!pageSize) + queryString['pageSize'] = pageSize; + + const queryStringItems = collection.map(queryString, (v, k) => `${k}=${v}`); + const queryStringText = queryStringItems.length > 0 ? `?${queryStringItems.join('&')}` : ''; + const response = await httpClient.get>(`v1/workflow-instances${queryStringText}`); + return response.data; + }, + get: async id => { + const response = await httpClient.get(`v1/workflow-instances/${id}`); + return response.data; + }, + delete: async id => { + await httpClient.delete(`v1/workflow-instances/${id}`); + }, + bulkDelete: async request => { + const response = await httpClient.delete(`v1/workflow-instances/bulk`, { + data: request + }); + return response.data; + } + }, + scriptingApi: { + getJavaScriptTypeDefinitions: async (workflowDefinitionId: string, context?: string): Promise => { + context = context || ''; + const response = await httpClient.get(`v1/scripting/javascript/type-definitions/${workflowDefinitionId}?t=${new Date().getTime()}&context=${context}`); + return response.data; + } } + } } export interface ElsaClient { - activitiesApi: ActivitiesApi; - workflowDefinitionsApi: WorkflowDefinitionsApi; - workflowRegistryApi: WorkflowRegistryApi; - workflowInstancesApi: WorkflowInstancesApi; - scriptingApi: ScriptingApi; + activitiesApi: ActivitiesApi; + workflowDefinitionsApi: WorkflowDefinitionsApi; + workflowRegistryApi: WorkflowRegistryApi; + workflowInstancesApi: WorkflowInstancesApi; + scriptingApi: ScriptingApi; } export interface ActivitiesApi { - list(): Promise>; + list(): Promise>; } export interface WorkflowDefinitionsApi { - getByDefinitionAndVersion(definitionId: string, versionOptions: VersionOptions): Promise; - list(page?: number, pageSize?: number, versionOptions?: VersionOptions): Promise>; + list(page?: number, pageSize?: number, versionOptions?: VersionOptions): Promise>; - save(request: SaveWorkflowDefinitionRequest): Promise; + getByDefinitionAndVersion(definitionId: string, versionOptions: VersionOptions): Promise; - delete(definitionId: string): Promise; + save(request: SaveWorkflowDefinitionRequest): Promise; - retract(workflowDefinitionId: string): Promise; + delete(definitionId: string): Promise; - export(workflowDefinitionId: string, versionOptions: VersionOptions): Promise; + retract(workflowDefinitionId: string): Promise; - import(workflowDefinitionId: string, file: File): Promise; + export(workflowDefinitionId: string, versionOptions: VersionOptions): Promise; + + import(workflowDefinitionId: string, file: File): Promise; } export interface WorkflowRegistryApi { - list(page?: number, pageSize?: number, versionOptions?: VersionOptions): Promise>; + list(page?: number, pageSize?: number, versionOptions?: VersionOptions): Promise>; + + get(id: string, versionOptions: VersionOptions): Promise; } export interface WorkflowInstancesApi { - list(page?: number, pageSize?: number, workflowDefinitionId?: string, workflowStatus?: WorkflowStatus, orderBy?: OrderBy, searchTerm?: string): Promise>; + list(page?: number, pageSize?: number, workflowDefinitionId?: string, workflowStatus?: WorkflowStatus, orderBy?: OrderBy, searchTerm?: string): Promise>; - delete(id: string): Promise; + get(id: string): Promise; - bulkDelete(request: BulkDeleteWorkflowsRequest): Promise; + delete(id: string): Promise; + + bulkDelete(request: BulkDeleteWorkflowsRequest): Promise; } export interface BulkDeleteWorkflowsRequest { - workflowInstanceIds: Array; + workflowInstanceIds: Array; } export interface BulkDeleteWorkflowsResponse { - deletedWorkflowCount: number; + deletedWorkflowCount: number; } export interface ScriptingApi { - getJavaScriptTypeDefinitions(workflowDefinitionId: string, context?: string): Promise + getJavaScriptTypeDefinitions(workflowDefinitionId: string, context?: string): Promise } export interface SaveWorkflowDefinitionRequest { - workflowDefinitionId?: string; - name?: string; - displayName?: string; - description?: string; - variables?: Variables; - contextOptions?: WorkflowContextOptions; - isSingleton?: boolean; - persistenceBehavior?: WorkflowPersistenceBehavior; - deleteCompletedInstances?: boolean; - publish?: boolean; - activities: Array; - connections: Array; + workflowDefinitionId?: string; + name?: string; + displayName?: string; + description?: string; + variables?: Variables; + contextOptions?: WorkflowContextOptions; + isSingleton?: boolean; + persistenceBehavior?: WorkflowPersistenceBehavior; + deleteCompletedInstances?: boolean; + publish?: boolean; + activities: Array; + connections: Array; } export interface ExportWorkflowResponse { - fileName: string; - data: Blob; + fileName: string; + data: Blob; }