feat(core): Add execution runData recovery and status field (#5112)

* adds ExecutionEvents view modal to ExecutionList

* fix time rendering and remove wf column

* checks for unfinished executions and fails them

* prevent re-setting stoppedAt for execution

* some cleanup / manually create rundata after crash

* quicksave

* remove Threads lib, log worker rewrite

* cleanup comment

* fix sentry destination return value

* test for tests...

* run tests with single worker

* fix tests

* remove console log

* add endpoint for execution data recovery

* lint cleanup and some refactoring

* fix accidental recursion

* remove cyclic imports

* add rundata recovery to Workflowrunner

* remove comments

* cleanup and refactor

* adds a status field to executions

* setExecutionStatus on queued worker

* fix onWorkflowPostExecute

* set waiting from worker

* get crashed status into frontend

* remove comment

* merge fix

* cleanup

* catch empty rundata in recovery

* refactor IExecutionsSummary and inject nodeExecution Errors

* reduce default event log size to 10mb from 100mb

* add per node execution status

* lint fix

* merge and lint fix

* phrasing change

* improve preview rendering and messaging

* remove debug

* Improve partial rundata recovery

* fix labels

* fix line through

* send manual rundata to ui at crash

* some type and msg push fixes

* improve recovered item rendering in preview

* update workflowStatistics on recover

* merge fix

* review fixes

* merge fix

* notify eventbus when ui is back up

* add a small timeout to make sure the UI is back up

* increase reconnect timeout to 30s

* adjust recover timeout and ui connection lost msg

* do not stop execution in editor after x reconnects

* add executionRecovered push event

* fix recovered connection not green

* remove reconnect toast and  merge existing rundata

* merge editor and recovered data for own mode
This commit is contained in:
Michael Auerswald
2023-02-17 10:54:07 +01:00
committed by GitHub
parent 3a9c257f55
commit d143f3f2ec
71 changed files with 1245 additions and 307 deletions

View File

@@ -264,7 +264,7 @@ import {
IExecutionsSummary,
IWorkflowShortResponse,
} from '@/Interface';
import { IDataObject } from 'n8n-workflow';
import type { ExecutionStatus, IDataObject } from 'n8n-workflow';
import { range as _range } from 'lodash';
import mixins from 'vue-typed-mixins';
import { mapStores } from 'pinia';
@@ -272,8 +272,6 @@ import { useUIStore } from '@/stores/ui';
import { useWorkflowsStore } from '@/stores/workflows';
import { setPageTitle } from '@/utils';
type ExecutionStatus = 'failed' | 'success' | 'waiting' | 'running' | 'unknown';
export default mixins(externalHooks, genericHelpers, executionHelpers, restApi, showMessage).extend(
{
name: 'ExecutionsList',
@@ -337,6 +335,14 @@ export default mixins(externalHooks, genericHelpers, executionHelpers, restApi,
id: 'error',
name: this.$locale.baseText('executionsList.error'),
},
{
id: 'crashed',
name: this.$locale.baseText('executionsList.error'),
},
{
id: 'new',
name: this.$locale.baseText('executionsList.new'),
},
{
id: 'running',
name: this.$locale.baseText('executionsList.running'),
@@ -357,13 +363,12 @@ export default mixins(externalHooks, genericHelpers, executionHelpers, restApi,
combinedExecutions(): IExecutionsSummary[] {
const returnData: IExecutionsSummary[] = [];
if (['ALL', 'running'].includes(this.filter.status)) {
if (['ALL', 'running', 'new'].includes(this.filter.status)) {
returnData.push(...this.activeExecutions);
}
if (['ALL', 'error', 'success', 'waiting'].includes(this.filter.status)) {
if (['ALL', 'error', 'crashed', 'success', 'waiting'].includes(this.filter.status)) {
returnData.push(...this.finishedExecutions);
}
return returnData;
},
combinedExecutionsCount(): number {
@@ -391,16 +396,31 @@ export default mixins(externalHooks, genericHelpers, executionHelpers, restApi,
return filter;
},
workflowFilterPast(): IDataObject {
const filter: IDataObject = {};
const queryFilter: IDataObject = {};
if (this.filter.workflowId !== 'ALL') {
filter.workflowId = this.filter.workflowId;
queryFilter.workflowId = this.filter.workflowId;
}
if (this.filter.status === 'waiting') {
filter.waitTill = true;
} else if (['error', 'success'].includes(this.filter.status)) {
filter.finished = this.filter.status === 'success';
switch (this.filter.status as ExecutionStatus) {
case 'waiting':
queryFilter.status = ['waiting'];
break;
case 'crashed':
queryFilter.status = ['crashed'];
break;
case 'new':
queryFilter.status = ['new'];
break;
case 'error':
queryFilter.status = ['failed', 'crashed', 'error'];
break;
case 'success':
queryFilter.status = ['success'];
break;
case 'running':
queryFilter.status = ['running'];
break;
}
return filter;
return queryFilter;
},
pageTitle() {
return this.$locale.baseText('executionsList.workflowExecutions');
@@ -793,19 +813,23 @@ export default mixins(externalHooks, genericHelpers, executionHelpers, restApi,
this.isDataLoading = false;
},
getStatus(execution: IExecutionsSummary): ExecutionStatus {
let status: ExecutionStatus = 'unknown';
if (execution.waitTill) {
status = 'waiting';
} else if (execution.stoppedAt === undefined) {
status = 'running';
} else if (execution.finished) {
status = 'success';
} else if (execution.stoppedAt !== null) {
status = 'failed';
} else {
status = 'unknown';
if (execution.status) return execution.status;
else {
// this should not happen but just in case
let status: ExecutionStatus = 'unknown';
if (execution.waitTill) {
status = 'waiting';
} else if (execution.stoppedAt === undefined) {
status = 'running';
} else if (execution.finished) {
status = 'success';
} else if (execution.stoppedAt !== null) {
status = 'failed';
} else {
status = 'unknown';
}
return status;
}
return status;
},
getRowClass(execution: IExecutionsSummary): string {
return [this.$style.execRow, this.$style[this.getStatus(execution)]].join(' ');
@@ -816,6 +840,10 @@ export default mixins(externalHooks, genericHelpers, executionHelpers, restApi,
if (status === 'waiting') {
text = this.$locale.baseText('executionsList.waiting');
} else if (status === 'crashed') {
text = this.$locale.baseText('executionsList.error');
} else if (status === 'new') {
text = this.$locale.baseText('executionsList.new');
} else if (status === 'running') {
text = this.$locale.baseText('executionsList.running');
} else if (status === 'success') {
@@ -834,6 +862,10 @@ export default mixins(externalHooks, genericHelpers, executionHelpers, restApi,
if (status === 'waiting') {
path = 'executionsList.statusWaiting';
} else if (status === 'crashed') {
path = 'executionsList.statusText';
} else if (status === 'new') {
path = 'executionsList.statusNew';
} else if (status === 'running') {
path = 'executionsList.statusRunning';
} else if (status === 'success') {
@@ -990,6 +1022,10 @@ export default mixins(externalHooks, genericHelpers, executionHelpers, restApi,
color: var(--color-danger);
}
.crashed & {
color: var(--color-danger);
}
.waiting & {
color: var(--color-secondary);
}
@@ -1099,6 +1135,10 @@ export default mixins(externalHooks, genericHelpers, executionHelpers, restApi,
background: var(--color-danger);
}
&.crashed td:first-child::before {
background: var(--color-danger);
}
&.success td:first-child::before {
background: var(--color-success);
}