Push active executions to clients to remove manual reload

This commit is contained in:
Jan Oberhauser 2019-07-24 14:25:30 +02:00
parent a9453806b8
commit 2d8038669a
12 changed files with 294 additions and 161 deletions

View file

@ -153,7 +153,8 @@ export interface IExecutionsStopData {
}
export interface IExecutionsSummary {
id: string;
id?: string; // executionIdDb
idActive?: string; // executionIdActive
mode: WorkflowExecuteMode;
finished?: boolean;
retryOf?: string;
@ -238,14 +239,23 @@ export interface IPushData {
type: IPushDataType;
}
export type IPushDataType = 'executionFinished' | 'nodeExecuteAfter' | 'nodeExecuteBefore' | 'testWebhookDeleted' | 'testWebhookReceived';
export type IPushDataType = 'executionFinished' | 'executionStarted' | 'nodeExecuteAfter' | 'nodeExecuteBefore' | 'testWebhookDeleted' | 'testWebhookReceived';
export interface IPushDataExecutionFinished {
data: IRun;
executionId: string;
executionIdActive: string;
executionIdDb?: string;
}
export interface IPushDataExecutionStarted {
executionId: string;
mode: WorkflowExecuteMode;
startedAt: Date;
retryOf?: string;
workflowId: string;
workflowName?: string;
}
export interface IPushDataNodeExecuteAfter {
data: ITaskData;

View file

@ -59,8 +59,11 @@ export class Push {
* @param {*} data
* @memberof Push
*/
send(sessionId: string, type: IPushDataType, data: any) { // tslint:disable-line:no-any
if (this.connections[sessionId] === undefined) {
send(type: IPushDataType, data: any, sessionId?: string) { // tslint:disable-line:no-any
if (sessionId !== undefined && this.connections[sessionId] === undefined) {
// TODO: Log that properly!
console.error(`The session "${sessionId}" is not registred.`);
return;
@ -71,7 +74,14 @@ export class Push {
data,
};
this.channel.send(JSON.stringify(sendData), [this.connections[sessionId]]);
if (sessionId === undefined) {
// Send to all connected clients
this.channel.send(JSON.stringify(sendData));
} else {
// Send only to a specific client
this.channel.send(JSON.stringify(sendData), [this.connections[sessionId]]);
}
}
}

View file

@ -848,7 +848,7 @@ class App {
}
returnData.push(
{
id: data.id.toString(),
idActive: data.id.toString(),
workflowId: data.workflowId,
mode:data.mode,
startedAt: new Date(data.startedAt),

View file

@ -91,7 +91,7 @@ export class TestWebhooks {
// Inform editor-ui that webhook got received
if (this.testWebhookData[webhookKey].sessionId !== undefined) {
pushInstance.send(this.testWebhookData[webhookKey].sessionId!, 'testWebhookReceived', { workflowId: webhookData.workflow.id });
pushInstance.send('testWebhookReceived', { workflowId: webhookData.workflow.id }, this.testWebhookData[webhookKey].sessionId!);
}
} catch (error) {
@ -167,7 +167,7 @@ export class TestWebhooks {
// Inform editor-ui that webhook got received
if (this.testWebhookData[webhookKey].sessionId !== undefined) {
try {
pushInstance.send(this.testWebhookData[webhookKey].sessionId!, 'testWebhookDeleted', { workflowId });
pushInstance.send('testWebhookDeleted', { workflowId }, this.testWebhookData[webhookKey].sessionId!);
} catch (error) {
// Could not inform editor, probably is not connected anymore. So sipmly go on.
}

View file

@ -3,6 +3,7 @@ import {
IExecutionDb,
IExecutionFlattedDb,
IPushDataExecutionFinished,
IPushDataExecutionStarted,
IPushDataNodeExecuteAfter,
IPushDataNodeExecuteBefore,
IWorkflowBase,
@ -60,11 +61,45 @@ function executeErrorWorkflow(workflowData: IWorkflowBase, fullRunData: IRun, mo
}
/**
* Pushes the execution out to all connected clients
*
* @param {IRun} fullRunData The RunData of the finished execution
* @param {string} executionIdActive The id of the finished execution
* @param {string} [executionIdDb] The database id of finished execution
*/
function pushExecutionFinished(fullRunData: IRun, executionIdActive: string, executionIdDb?: string) {
// Clone the object except the runData. That one is not supposed
// to be send. Because that data got send piece by piece after
// each node which finished executing
const pushRunData = {
...fullRunData,
data: {
...fullRunData.data,
resultData: {
...fullRunData.data.resultData,
runData: {},
},
},
};
// Push data to editor-ui once workflow finished
const sendData: IPushDataExecutionFinished = {
executionIdActive,
executionIdDb,
data: pushRunData,
};
pushInstance.send('executionFinished', sendData);
}
const hooks = (mode: WorkflowExecuteMode, workflowData: IWorkflowBase, workflowInstance: Workflow, sessionId?: string, retryOf?: string) => {
return {
nodeExecuteBefore: [
async (executionId: string, nodeName: string): Promise<void> => {
if (sessionId === undefined) {
// Only push data to the session which started it
return;
}
@ -73,7 +108,7 @@ const hooks = (mode: WorkflowExecuteMode, workflowData: IWorkflowBase, workflowI
nodeName,
};
pushInstance.send(sessionId, 'nodeExecuteBefore', sendData);
pushInstance.send('nodeExecuteBefore', sendData, sessionId);
},
],
nodeExecuteAfter: [
@ -88,36 +123,27 @@ const hooks = (mode: WorkflowExecuteMode, workflowData: IWorkflowBase, workflowI
data,
};
pushInstance.send(sessionId, 'nodeExecuteAfter', sendData);
pushInstance.send('nodeExecuteAfter', sendData, sessionId);
},
],
workflowExecuteBefore: [
async (executionId: string): Promise<void> => {
// Push data to editor-ui once workflow finished
const sendData: IPushDataExecutionStarted = {
executionId,
mode,
startedAt: new Date(),
retryOf,
workflowId: workflowData.id as string,
workflowName: workflowData.name,
};
pushInstance.send('executionStarted', sendData);
}
],
workflowExecuteAfter: [
async (fullRunData: IRun, executionId: string): Promise<void> => {
try {
if (sessionId !== undefined) {
// Clone the object except the runData. That one is not supposed
// to be send. Because that data got send piece by piece after
// each node which finished executing
const pushRunData = {
...fullRunData,
data: {
...fullRunData.data,
resultData: {
...fullRunData.data.resultData,
runData: {},
},
},
};
// Push data to editor-ui once workflow finished
const sendData: IPushDataExecutionFinished = {
executionId,
data: pushRunData,
};
pushInstance.send(sessionId, 'executionFinished', sendData);
}
const workflowSavePromise = WorkflowHelpers.saveStaticData(workflowInstance);
let saveManualExecutions = config.get('executions.saveDataManualExecutions') as boolean;
@ -132,6 +158,7 @@ const hooks = (mode: WorkflowExecuteMode, workflowData: IWorkflowBase, workflowI
await workflowSavePromise;
}
pushExecutionFinished(fullRunData, executionId);
executeErrorWorkflow(workflowData, fullRunData, mode);
return;
}
@ -148,6 +175,7 @@ const hooks = (mode: WorkflowExecuteMode, workflowData: IWorkflowBase, workflowI
if (workflowDidSucceed === true && saveDataSuccessExecution === 'none' ||
workflowDidSucceed === false && saveDataErrorExecution === 'none'
) {
pushExecutionFinished(fullRunData, executionId);
executeErrorWorkflow(workflowData, fullRunData, mode);
return;
}
@ -185,8 +213,10 @@ const hooks = (mode: WorkflowExecuteMode, workflowData: IWorkflowBase, workflowI
await workflowSavePromise;
}
pushExecutionFinished(fullRunData, executionId, executionResult.id as string);
executeErrorWorkflow(workflowData, fullRunData, mode, executionResult ? executionResult.id as string : undefined);
} catch (error) {
pushExecutionFinished(fullRunData, executionId);
executeErrorWorkflow(workflowData, fullRunData, mode);
}
},

View file

@ -294,7 +294,8 @@ export interface IExecutionsListResponse {
}
export interface IExecutionsCurrentSummaryExtended {
id: string;
id?: string;
idActive: string;
finished?: boolean;
mode: WorkflowExecuteMode;
startedAt: Date;
@ -311,7 +312,8 @@ export interface IExecutionsStopData {
}
export interface IExecutionsSummary {
id: string;
id?: string; // executionIdDb
idActive?: string; // executionIdActive
mode: WorkflowExecuteMode;
finished?: boolean;
retryOf?: string;
@ -333,10 +335,25 @@ export interface IPushData {
type: IPushDataType;
}
export type IPushDataType = 'executionFinished' | 'nodeExecuteAfter' | 'nodeExecuteBefore' | 'testWebhookDeleted' | 'testWebhookReceived';
export type IPushDataType = 'executionFinished' | 'executionStarted' | 'nodeExecuteAfter' | 'nodeExecuteBefore' | 'testWebhookDeleted' | 'testWebhookReceived';
export interface IPushDataExecutionStarted {
executionId: string;
mode: WorkflowExecuteMode;
startedAt: Date;
retryOf?: string;
workflowId: string;
workflowName?: string;
}
export interface IPushDataExecutionFinished {
data: IRun;
executionIdActive: string;
executionIdDb?: string;
}
export interface IPushDataExecutionStarted {
executionId: string;
}

View file

@ -0,0 +1,64 @@
<template>
<span>
{{time}}
</span>
</template>
<script lang="ts">
import Vue from 'vue';
import { genericHelpers } from '@/components/mixins/genericHelpers';
import mixins from 'vue-typed-mixins';
export default mixins(
genericHelpers,
)
.extend({
name: 'ExecutionTime',
props: [
'startTime',
],
computed: {
time (): string {
if (!this.startTime) {
return '...';
}
const msPassed = this.nowTime - new Date(this.startTime).getTime();
return this.displayTimer(msPassed);
},
},
data () {
return {
nowTime: -1,
intervalTimer: null as null | NodeJS.Timeout,
};
},
mounted () {
this.setNow();
this.intervalTimer = setInterval(() => {
this.setNow();
}, 1000);
},
destroyed () {
// Make sure that the timer gets destroyed once no longer needed
if (this.intervalTimer !== null) {
clearInterval(this.intervalTimer);
}
},
methods: {
setNow () {
this.nowTime = (new Date()).getTime();
},
},
});
</script>
<style lang="scss">
// .data-display-wrapper {
// }
</style>

View file

@ -16,25 +16,7 @@
</el-option>
</el-select>
</el-col>
<el-col :span="3">&nbsp;
</el-col>
<el-col :span="3" class="filter-headline">
Auto-Refresh:
</el-col>
<el-col :span="4">
<el-select v-model="autoRefresh.time" placeholder="Select Refresh Time" size="small" filterable @change="handleRefreshTimeChanged">
<el-option
v-for="item in autoRefresh.options"
:key="item.value"
:label="item.name"
:value="item.value">
</el-option>
</el-select>
</el-col>
<el-col :span="4">
<el-button title="Refresh" @click="refreshData()" :disabled="isDataLoading" size="small" type="success" class="refresh-button">
<font-awesome-icon icon="sync" /> Manual Refresh
</el-button>
<el-col :span="14">&nbsp;
</el-col>
</el-row>
</div>
@ -46,26 +28,27 @@
</span>
</div>
<el-table :data="combinedExecutions" stripe v-loading="isDataLoading" :row-class-name="getRowClass" @row-click="handleRowClick">
<el-table :data="combinedExecutions" stripe v-loading="isDataLoading" :row-class-name="getRowClass">
<el-table-column label="" width="30">
<!-- eslint-disable-next-line vue/no-unused-vars -->
<template slot="header" slot-scope="scope">
<el-checkbox :indeterminate="isIndeterminate" v-model="checkAll" @change="handleCheckAllChange">Check all</el-checkbox>
</template>
<template slot-scope="scope">
<el-checkbox v-if="scope.row.stoppedAt !== undefined" :value="selectedItems[scope.row.id.toString()] || checkAll" @change="handleCheckboxChanged(scope.row.id)" >Check all</el-checkbox>
<el-checkbox v-if="scope.row.stoppedAt !== undefined && scope.row.id" :value="selectedItems[scope.row.id.toString()] || checkAll" @change="handleCheckboxChanged(scope.row.id)" >Check all</el-checkbox>
</template>
</el-table-column>
<el-table-column property="startedAt" label="Started At / ID" width="205">
<template slot-scope="scope">
{{convertToDisplayDate(scope.row.startedAt)}}<br />
<small>ID: {{scope.row.id}}</small>
<small v-if="scope.row.id">ID: {{scope.row.id}}</small>
<small v-if="scope.row.idActive && scope.row.id === undefined && scope.row.stoppedAt === undefined">Active-ID: {{scope.row.idActive}}</small>
</template>
</el-table-column>
<el-table-column property="workflowName" label="Name">
<template slot-scope="scope">
<span class="workflow-name">
{{scope.row.workflowName}}
{{scope.row.workflowName || '[UNSAVED WORKFLOW]'}}
</span>
<span v-if="scope.row.stoppedAt === undefined">
@ -107,21 +90,21 @@
<template slot-scope="scope">
<span v-if="scope.row.stoppedAt === undefined">
<font-awesome-icon icon="spinner" spin />
{{(new Date().getTime() - new Date(scope.row.startedAt).getTime())/1000}} sec.
<execution-time :start-time="scope.row.startedAt"/>
</span>
<span v-else>
{{(new Date(scope.row.stoppedAt).getTime() - new Date(scope.row.startedAt).getTime()) / 1000}} sec.
{{ displayTimer(new Date(scope.row.stoppedAt).getTime() - new Date(scope.row.startedAt).getTime(), true) }}
</span>
</template>
</el-table-column>
<el-table-column label="" width="100" align="center">
<template slot-scope="scope">
<span v-if="scope.row.stoppedAt === undefined">
<el-button circle title="Stop Execution" @click.stop="stopExecution(scope.row.id)" :loading="stoppingExecutions.includes(scope.row.id)" size="mini">
<span v-if="scope.row.stoppedAt === undefined && scope.row.idActive">
<el-button circle title="Stop Execution" @click.stop="stopExecution(scope.row.idActive)" :loading="stoppingExecutions.includes(scope.row.idActive)" size="mini">
<font-awesome-icon icon="stop" />
</el-button>
</span>
<span v-else>
<span v-else-if="scope.row.id">
<el-button circle title="Open Past Execution" @click.stop="displayExecution(scope.row)" size="mini">
<font-awesome-icon icon="folder-open" />
</el-button>
@ -143,6 +126,7 @@
<script lang="ts">
import Vue from 'vue';
import ExecutionTime from '@/components/ExecutionTime.vue';
import WorkflowActivator from '@/components/WorkflowActivator.vue';
import { restApi } from '@/components/mixins/restApi';
@ -174,52 +158,16 @@ export default mixins(
'dialogVisible',
],
components: {
ExecutionTime,
WorkflowActivator,
},
data () {
return {
activeExecutions: [] as IExecutionsCurrentSummaryExtended[],
finishedExecutions: [] as IExecutionsSummary[],
finishedExecutionsCount: 0,
checkAll: false,
autoRefresh: {
timer: undefined as NodeJS.Timeout | undefined,
time: -1,
options: [
{
name: 'Deactivated',
value: -1,
},
{
name: '5 Seconds',
value: 5,
},
{
name: '10 Seconds',
value: 10,
},
{
name: '15 Seconds',
value: 15,
},
{
name: '30 Seconds',
value: 30,
},
{
name: '1 Minute',
value: 60,
},
{
name: '5 Minutes',
value: 300,
},
],
},
filter: {
workflowId: 'ALL',
},
@ -236,15 +184,13 @@ export default mixins(
};
},
computed: {
activeExecutions (): IExecutionsCurrentSummaryExtended[] {
return this.$store.getters.getActiveExecutions;
},
combinedExecutions (): IExecutionsSummary[] {
const returnData: IExecutionsSummary[] = [];
// The active executions do not have the workflow-names yet so add them
for (const executionData of this.activeExecutions) {
executionData.workflowName = this.getWorkflowName(executionData.workflowId);
returnData.push(executionData);
}
returnData.push.apply(returnData, this.activeExecutions);
returnData.push.apply(returnData, this.finishedExecutions);
return returnData;
@ -281,8 +227,6 @@ export default mixins(
dialogVisible (newValue, oldValue) {
if (newValue) {
this.openDialog();
} else {
this.handleRefreshTimeChanged(-1);
}
},
},
@ -355,23 +299,30 @@ export default mixins(
this.refreshData();
},
getRowClass (data: IDataObject): string {
const classes: string[] = ['clickable'];
const classes: string[] = [];
if ((data.row as IExecutionsSummary).stoppedAt === undefined) {
classes.push('currently-running');
}
return classes.join(' ');
},
getWorkflowName (workflowId: string): string {
getWorkflowName (workflowId: string): string | undefined {
const workflow = this.workflows.find((data) => data.id === workflowId);
if (workflow === undefined) {
return '<UNSAVED WORKFLOW>';
return undefined;
}
return workflow.name;
},
async loadActiveExecutions (): Promise<void> {
this.activeExecutions = await this.restApi().getCurrentExecutions(this.workflowFilter);
const activeExecutions = await this.restApi().getCurrentExecutions(this.workflowFilter);
for (const activeExecution of activeExecutions) {
if (activeExecution.workflowId !== undefined && activeExecution.workflowName === undefined) {
activeExecution.workflowName = this.getWorkflowName(activeExecution.workflowId);
}
}
this.$store.commit('setActiveExecutions', activeExecutions);
},
async loadFinishedExecutions (): Promise<void> {
const data = await this.restApi().getPastExecutions(this.workflowFilter, this.requestItemsPerRequest);
@ -379,11 +330,6 @@ export default mixins(
this.finishedExecutionsCount = data.count;
},
async loadMore () {
// Deactivate the auto-refresh because else the newly displayed
// data would be lost with the next automatic refresh
this.autoRefresh.time = -1;
this.handleRefreshTimeChanged();
this.isDataLoading = true;
const filter = this.workflowFilter;
@ -432,32 +378,13 @@ export default mixins(
this.$showError(error, 'Problem loading workflows', 'There was a problem loading the workflows:');
}
},
openDialog () {
async openDialog () {
Vue.set(this, 'selectedItems', {});
this.filter.workflowId = 'ALL';
this.checkAll = false;
this.loadWorkflows();
this.refreshData();
this.handleRefreshTimeChanged();
},
handleRefreshTimeChanged (manualOverwrite?: number) {
if (this.autoRefresh.timer !== undefined) {
// Make sure the old timer gets removed
clearInterval(this.autoRefresh.timer);
this.autoRefresh.timer = undefined;
}
const timerValue = manualOverwrite !== undefined ? manualOverwrite : this.autoRefresh.time;
if (timerValue === -1) {
// No timer should be set
return;
}
// Create the new interval timer
this.autoRefresh.timer = setInterval(() => {
this.refreshData();
}, timerValue * 1000);
await this.loadWorkflows();
await this.refreshData();
},
async retryExecution (execution: IExecutionShortResponse) {
this.isDataLoading = true;
@ -501,18 +428,6 @@ export default mixins(
this.isDataLoading = false;
},
handleRowClick (entry: IExecutionsSummary, event: Event, column: any) { // tslint:disable-line:no-any
if (column.label === '') {
// Ignore all clicks in the first and last row
return;
}
if (this.selectedItems[entry.id]) {
Vue.delete(this.selectedItems, entry.id);
} else {
Vue.set(this.selectedItems, entry.id, true);
}
},
statusTooltipText (entry: IExecutionsSummary): string {
if (entry.stoppedAt === undefined) {
return 'The worklow is currently executing.';
@ -526,21 +441,21 @@ export default mixins(
return 'The workflow execution did fail.';
}
},
async stopExecution (executionId: string) {
async stopExecution (activeExecutionId: string) {
try {
// Add it to the list of currently stopping executions that we
// can show the user in the UI that it is in progress
this.stoppingExecutions.push(executionId);
this.stoppingExecutions.push(activeExecutionId);
const stopData: IExecutionsStopData = await this.restApi().stopCurrentExecution(executionId);
const stopData: IExecutionsStopData = await this.restApi().stopCurrentExecution(activeExecutionId);
// Remove it from the list of currently stopping executions
const index = this.stoppingExecutions.indexOf(executionId);
const index = this.stoppingExecutions.indexOf(activeExecutionId);
this.stoppingExecutions.splice(index, 1);
this.$showMessage({
title: 'Execution stopped',
message: `The execution with the id "${executionId}" got stopped!`,
message: `The execution with the id "${activeExecutionId}" got stopped!`,
type: 'success',
});

View file

@ -23,7 +23,21 @@ export const genericHelpers = mixins(showMessage).extend({
convertToDisplayDate (epochTime: number) {
return dateformat(epochTime, 'yyyy-mm-dd HH:MM:ss');
},
displayTimer (msPassed: number, showMs = false): string {
if (msPassed < 60000) {
if (showMs === false) {
return `${Math.floor(msPassed / 1000)} sec.`;
}
return `${msPassed / 1000} sec.`;
}
const secondsPassed = Math.floor(msPassed / 1000);
const minutesPassed = Math.floor(secondsPassed / 60);
const secondsLeft = (secondsPassed - (minutesPassed * 60)).toString().padStart(2, '0');
return `${minutesPassed}:${secondsLeft} min.`;
},
editAllowedCheck (): boolean {
if (this.isReadOnly) {
this.$showMessage({

View file

@ -161,7 +161,6 @@ export const mouseSelect = mixins(nodeIndex).extend({
const nodeElement = `node-${this.getNodeIndex(node.name)}`;
// @ts-ignore
this.instance.removeFromDragSelection(nodeElement);
},
nodeSelected (node: INodeUi) {
this.$store.commit('addSelectedNode', node);

View file

@ -1,6 +1,8 @@
import {
IExecutionsCurrentSummaryExtended,
IPushData,
IPushDataExecutionFinished,
IPushDataExecutionStarted,
IPushDataNodeExecuteAfter,
IPushDataNodeExecuteBefore,
IPushDataTestWebhook,
@ -100,7 +102,7 @@ export const pushConnection = mixins(
return;
}
if (['executionFinished', 'nodeExecuteAfter', 'nodeExecuteBefore'].includes(receivedData.type)) {
if (['nodeExecuteAfter', 'nodeExecuteBefore'].includes(receivedData.type)) {
if (this.$store.getters.isActionActive('workflowRunning') === false) {
// No workflow is running so ignore the messages
return;
@ -119,6 +121,19 @@ export const pushConnection = mixins(
// The workflow finished executing
const pushData = receivedData.data as IPushDataExecutionFinished;
this.$store.commit('finishActiveExecution', pushData);
if (this.$store.getters.isActionActive('workflowRunning') === false) {
// No workflow is running so ignore the messages
return;
}
if (this.$store.getters.activeExecutionId !== pushData.executionIdActive) {
// The workflow which did finish execution did not get started
// by this session so ignore
return;
}
const runDataExecuted = pushData.data;
if (runDataExecuted.finished !== true) {
@ -149,6 +164,19 @@ export const pushConnection = mixins(
// Set the node execution issues on all the nodes which produced an error so that
// it can be displayed in the node-view
this.updateNodesExecutionIssues();
} else if (receivedData.type === 'executionStarted') {
const pushData = receivedData.data as IPushDataExecutionStarted;
const executionData: IExecutionsCurrentSummaryExtended = {
idActive: pushData.executionId,
finished: false,
mode: pushData.mode,
startedAt: pushData.startedAt,
workflowId: pushData.workflowId,
workflowName: pushData.workflowName,
};
this.$store.commit('addActiveExecution', executionData);
} else if (receivedData.type === 'nodeExecuteAfter') {
// A node finished to execute. Add its data
const pushData = receivedData.data as IPushDataNodeExecuteAfter;

View file

@ -19,6 +19,8 @@ import {
import {
ICredentialsResponse,
IExecutionResponse,
IExecutionsCurrentSummaryExtended,
IPushDataExecutionFinished,
IPushDataNodeExecuteAfter,
IWorkflowDb,
INodeUi,
@ -34,6 +36,7 @@ Vue.use(Vuex);
export const store = new Vuex.Store({
strict: process.env.NODE_ENV !== 'production',
state: {
activeExecutions: [] as IExecutionsCurrentSummaryExtended[],
activeWorkflows: [] as string[],
activeActions: [] as string[],
activeNode: null as string | null,
@ -84,6 +87,45 @@ export const store = new Vuex.Store({
}
},
// Active Executions
addActiveExecution (state, newActiveExecution: IExecutionsCurrentSummaryExtended) {
// Check if the execution exists already
const activeExecution = state.activeExecutions.find(execution => {
return execution.idActive === newActiveExecution.idActive;
});
if (activeExecution !== undefined) {
// Exists already so no need to add it again
if (activeExecution.workflowName === undefined) {
activeExecution.workflowName = newActiveExecution.workflowName;
}
return;
}
state.activeExecutions.unshift(newActiveExecution);
},
finishActiveExecution (state, finishedActiveExecution: IPushDataExecutionFinished) {
// Find the execution to set to finished
const activeExecution = state.activeExecutions.find(execution => {
return execution.idActive === finishedActiveExecution.executionIdActive;
});
if (activeExecution === undefined) {
// The execution could not be found
return;
}
if (finishedActiveExecution.executionIdDb !== undefined) {
Vue.set(activeExecution, 'id', finishedActiveExecution.executionIdDb);
}
Vue.set(activeExecution, 'finished', finishedActiveExecution.data.finished);
Vue.set(activeExecution, 'stoppedAt', finishedActiveExecution.data.stoppedAt);
},
setActiveExecutions (state, newActiveExecutions: IExecutionsCurrentSummaryExtended[]) {
Vue.set(state, 'activeExecutions', newActiveExecutions);
},
// Active Workflows
setActiveWorkflows (state, newActiveWorkflows: string[]) {
state.activeWorkflows = newActiveWorkflows;
@ -506,6 +548,10 @@ export const store = new Vuex.Store({
return state.activeActions.includes(action);
},
getActiveExecutions: (state): IExecutionsCurrentSummaryExtended[] => {
return state.activeExecutions;
},
getBaseUrl: (state): string => {
return state.baseUrl;
},