Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): Improve paired item and add additional variables #3765

Merged
merged 24 commits into from
Sep 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
170c1be
:zap: Remove duplicate and old string
janober Jul 22, 2022
cbcd966
:zap: Add telemetry
janober Jul 23, 2022
910c5f0
:twisted_rightwards_arrows: Merge branch 'master' into paired-item-im…
janober Jul 31, 2022
dd3d174
:zap: Futher improvements
janober Jul 31, 2022
795553a
:twisted_rightwards_arrows: Merge branch 'master' into paired-item-im…
janober Aug 12, 2022
218a2a3
:zap: Change error message and display only name of last parameter
janober Aug 12, 2022
4cb03fb
:shirt: Fix lint issue
janober Aug 12, 2022
3267f7e
:zap: Remove not needed comments
janober Aug 12, 2022
3c1c8f6
:twisted_rightwards_arrows: Merge branch 'master' into paired-item-im…
janober Aug 22, 2022
0a08388
:zap: Rename properties, add new ones and improve error messages
janober Aug 26, 2022
31e952a
:twisted_rightwards_arrows: Merge branch 'master' into paired-item-im…
janober Aug 26, 2022
ac8c332
:zap: Add support for $execution, $prevNode and make it possible to u…
janober Aug 26, 2022
350b7de
:zap: Some small improvements
janober Aug 26, 2022
c1a7abe
:bug: Fix error message
janober Aug 30, 2022
b7cea96
:zap: Improve some error messages
janober Aug 30, 2022
be19b0f
:zap: Change resumeUrl variable and display in editor
janober Aug 30, 2022
81f1b93
:twisted_rightwards_arrows: Merge branch 'master' into paired-item-im…
janober Sep 10, 2022
481099d
:zap: Fix and extend tests
janober Sep 11, 2022
da75fe0
:twisted_rightwards_arrows: Merge branch 'master' into paired-item-im…
janober Sep 18, 2022
d6991db
:zap: Multiple pairedItem improvements
janober Sep 18, 2022
e865b6e
:zap: Display "More Info" link with error messages if user can fix issue
janober Sep 24, 2022
06ba25d
:zap: Display different errors in Function Nodes
janober Sep 24, 2022
8bb993a
:twisted_rightwards_arrows: Merge branch 'master' into paired-item-im…
janober Sep 26, 2022
e98057f
:twisted_rightwards_arrows: Merge branch 'master' into paired-item-im…
janober Sep 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions packages/cli/src/InternalHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,14 @@ export class InternalHooksClass implements IInternalHooksClass {

if (!properties.success && runData?.data.resultData.error) {
properties.error_message = runData?.data.resultData.error.message;
let errorNodeName = runData?.data.resultData.error.node?.name;
properties.error_node_type = runData?.data.resultData.error.node?.type;
let errorNodeName =
'node' in runData?.data.resultData.error
? runData?.data.resultData.error.node?.name
: undefined;
properties.error_node_type =
'node' in runData?.data.resultData.error
? runData?.data.resultData.error.node?.type
: undefined;

if (runData.data.resultData.lastNodeExecuted) {
const lastNode = TelemetryHelpers.getNodeTypeForName(
Expand Down
71 changes: 40 additions & 31 deletions packages/core/src/NodeExecuteFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1487,11 +1487,20 @@ export async function requestWithAuthentication(
*/
export function getAdditionalKeys(
additionalData: IWorkflowExecuteAdditionalData,
mode: WorkflowExecuteMode,
): IWorkflowDataProxyAdditionalKeys {
const executionId = additionalData.executionId || PLACEHOLDER_EMPTY_EXECUTION_ID;
const resumeUrl = `${additionalData.webhookWaitingBaseUrl}/${executionId}`;
return {
$execution: {
id: executionId,
mode: mode === 'manual' ? 'test' : 'production',
resumeUrl,
},

// deprecated
$executionId: executionId,
$resumeWebhookUrl: `${additionalData.webhookWaitingBaseUrl}/${executionId}`,
$resumeWebhookUrl: resumeUrl,
};
}

Expand Down Expand Up @@ -1601,7 +1610,7 @@ export async function getCredentials(
// TODO: solve using credentials via expression
// if (name.charAt(0) === '=') {
// // If the credential name is an expression resolve it
// const additionalKeys = getAdditionalKeys(additionalData);
// const additionalKeys = getAdditionalKeys(additionalData, mode);
// name = workflow.expression.getParameterValue(
// name,
// runExecutionData || null,
Expand Down Expand Up @@ -1638,30 +1647,29 @@ export function getNode(node: INode): INode {
* Clean up parameter data to make sure that only valid data gets returned
* INFO: Currently only converts Luxon Dates as we know for sure it will not be breaking
*/
function cleanupParameterData(inputData: NodeParameterValueType): NodeParameterValueType {
if (inputData === null || inputData === undefined) {
return inputData;
function cleanupParameterData(inputData: NodeParameterValueType): void {
if (typeof inputData !== 'object' || inputData === null) {
return;
}

if (Array.isArray(inputData)) {
inputData.forEach((value) => cleanupParameterData(value));
return inputData;
}

if (inputData.constructor.name === 'DateTime') {
// Is a special luxon date so convert to string
return inputData.toString();
return;
}

if (typeof inputData === 'object') {
Object.keys(inputData).forEach((key) => {
inputData[key as keyof typeof inputData] = cleanupParameterData(
inputData[key as keyof typeof inputData],
);
if (typeof inputData[key as keyof typeof inputData] === 'object') {
if (inputData[key as keyof typeof inputData]?.constructor.name === 'DateTime') {
// Is a special luxon date so convert to string
inputData[key as keyof typeof inputData] =
inputData[key as keyof typeof inputData]?.toString();
} else {
cleanupParameterData(inputData[key as keyof typeof inputData]);
}
}
});
}

return inputData;
}

/**
Expand Down Expand Up @@ -1710,7 +1718,7 @@ export function getNodeParameter(
executeData,
);

returnData = cleanupParameterData(returnData);
cleanupParameterData(returnData);
} catch (e) {
if (e.context) e.context.parameter = parameterName;
e.cause = value;
Expand Down Expand Up @@ -1883,7 +1891,7 @@ export function getExecutePollFunctions(
itemIndex,
mode,
additionalData.timezone,
getAdditionalKeys(additionalData),
getAdditionalKeys(additionalData, mode),
undefined,
fallbackValue,
options,
Expand Down Expand Up @@ -2032,7 +2040,7 @@ export function getExecuteTriggerFunctions(
itemIndex,
mode,
additionalData.timezone,
getAdditionalKeys(additionalData),
getAdditionalKeys(additionalData, mode),
undefined,
fallbackValue,
options,
Expand Down Expand Up @@ -2160,7 +2168,7 @@ export function getExecuteFunctions(
connectionInputData,
mode,
additionalData.timezone,
getAdditionalKeys(additionalData),
getAdditionalKeys(additionalData, mode),
executeData,
);
},
Expand Down Expand Up @@ -2237,7 +2245,7 @@ export function getExecuteFunctions(
itemIndex,
mode,
additionalData.timezone,
getAdditionalKeys(additionalData),
getAdditionalKeys(additionalData, mode),
executeData,
fallbackValue,
options,
Expand Down Expand Up @@ -2272,7 +2280,7 @@ export function getExecuteFunctions(
{},
mode,
additionalData.timezone,
getAdditionalKeys(additionalData),
getAdditionalKeys(additionalData, mode),
executeData,
);
return dataProxy.getDataProxy();
Expand Down Expand Up @@ -2421,7 +2429,7 @@ export function getExecuteSingleFunctions(
connectionInputData,
mode,
additionalData.timezone,
getAdditionalKeys(additionalData),
getAdditionalKeys(additionalData, mode),
executeData,
);
},
Expand Down Expand Up @@ -2501,7 +2509,7 @@ export function getExecuteSingleFunctions(
itemIndex,
mode,
additionalData.timezone,
getAdditionalKeys(additionalData),
getAdditionalKeys(additionalData, mode),
executeData,
fallbackValue,
options,
Expand All @@ -2521,7 +2529,7 @@ export function getExecuteSingleFunctions(
{},
mode,
additionalData.timezone,
getAdditionalKeys(additionalData),
getAdditionalKeys(additionalData, mode),
executeData,
);
return dataProxy.getDataProxy();
Expand Down Expand Up @@ -2658,6 +2666,7 @@ export function getLoadOptionsFunctions(
const runExecutionData: IRunExecutionData | null = null;
const itemIndex = 0;
const runIndex = 0;
const mode = 'internal' as WorkflowExecuteMode;
const connectionInputData: INodeExecutionData[] = [];

return getNodeParameter(
Expand All @@ -2668,9 +2677,9 @@ export function getLoadOptionsFunctions(
node,
parameterName,
itemIndex,
'internal' as WorkflowExecuteMode,
mode,
additionalData.timezone,
getAdditionalKeys(additionalData),
getAdditionalKeys(additionalData, mode),
undefined,
fallbackValue,
options,
Expand Down Expand Up @@ -2792,7 +2801,7 @@ export function getExecuteHookFunctions(
itemIndex,
mode,
additionalData.timezone,
getAdditionalKeys(additionalData),
getAdditionalKeys(additionalData, mode),
undefined,
fallbackValue,
options,
Expand All @@ -2806,7 +2815,7 @@ export function getExecuteHookFunctions(
additionalData,
mode,
additionalData.timezone,
getAdditionalKeys(additionalData),
getAdditionalKeys(additionalData, mode),
isTest,
);
},
Expand Down Expand Up @@ -2945,7 +2954,7 @@ export function getExecuteWebhookFunctions(
itemIndex,
mode,
additionalData.timezone,
getAdditionalKeys(additionalData),
getAdditionalKeys(additionalData, mode),
undefined,
fallbackValue,
options,
Expand Down Expand Up @@ -2983,7 +2992,7 @@ export function getExecuteWebhookFunctions(
additionalData,
mode,
additionalData.timezone,
getAdditionalKeys(additionalData),
getAdditionalKeys(additionalData, mode),
);
},
getTimezone: (): string => {
Expand Down
7 changes: 7 additions & 0 deletions packages/editor-ui/src/components/CodeEdit.vue
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ export default mixins(
const connectionInputData = this.connectionInputData(parentNode, activeNode!.name, inputName, runIndex, nodeConnection);

const additionalProxyKeys: IWorkflowDataProxyAdditionalKeys = {
$execution: {
id: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
mode: 'test',
resumeUrl: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
},

// deprecated
$executionId: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
$resumeWebhookUrl: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
};
Expand Down
20 changes: 13 additions & 7 deletions packages/editor-ui/src/components/Error/NodeErrorView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div>
<div class="error-header">
<div class="error-message">{{ $locale.baseText('nodeErrorView.error') + ': ' + getErrorMessage() }}</div>
<div class="error-description" v-if="error.description">{{getErrorDescription()}}</div>
<div class="error-description" v-if="error.description" v-html="getErrorDescription()"></div>
</div>
<details>
<summary class="error-details__summary">
Expand Down Expand Up @@ -139,28 +139,34 @@ export default mixins(
},
},
methods: {
replacePlaceholders (parameter: string, message: string): string {
const parameterName = this.parameterDisplayName(parameter, false);
const parameterFullName = this.parameterDisplayName(parameter, true);
return message.replace(/%%PARAMETER%%/g, parameterName).replace(/%%PARAMETER_FULL%%/g, parameterFullName);
},
getErrorDescription (): string {
if (!this.error.context || !this.error.context.descriptionTemplate) {
return this.error.description;
}

const parameterName = this.parameterDisplayName(this.error.context.parameter);
return this.error.context.descriptionTemplate.replace(/%%PARAMETER%%/g, parameterName);
return this.replacePlaceholders(this.error.context.parameter, this.error.context.descriptionTemplate);
},
getErrorMessage (): string {
if (!this.error.context || !this.error.context.messageTemplate) {
return this.error.message;
}

const parameterName = this.parameterDisplayName(this.error.context.parameter);
return this.error.context.messageTemplate.replace(/%%PARAMETER%%/g, parameterName);
return this.replacePlaceholders(this.error.context.parameter, this.error.context.messageTemplate);
},
parameterDisplayName(path: string) {
parameterDisplayName(path: string, fullPath = true) {
try {
const parameters = this.parameterName(this.parameters, path.split('.'));
if (!parameters.length) {
throw new Error();
}

if (fullPath === false) {
return parameters.pop()!.displayName;
}
return parameters.map(parameter => parameter.displayName).join(' > ');
} catch (error) {
return `Could not find parameter "${path}"`;
Expand Down
7 changes: 7 additions & 0 deletions packages/editor-ui/src/components/VariableSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,13 @@ export default mixins(
}

const additionalKeys: IWorkflowDataProxyAdditionalKeys = {
$execution: {
id: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
mode: 'test',
resumeUrl: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
},

// deprecated
$executionId: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
$resumeWebhookUrl: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
};
Expand Down
54 changes: 46 additions & 8 deletions packages/editor-ui/src/components/mixins/pushConnection.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import {
IExecutionsCurrentSummaryExtended,
IPushData,
IPushDataConsoleMessage,
IPushDataExecutionFinished,
IPushDataExecutionStarted,
IPushDataNodeExecuteAfter,
IPushDataNodeExecuteBefore,
IPushDataTestWebhook,
} from '../../Interface';

import { externalHooks } from '@/components/mixins/externalHooks';
Expand All @@ -16,7 +10,11 @@ import { titleChange } from '@/components/mixins/titleChange';
import { workflowHelpers } from '@/components/mixins/workflowHelpers';

import {
ExpressionError,
IDataObject,
INodeTypeNameVersion,
IWorkflowBase,
TelemetryHelpers,
} from 'n8n-workflow';

import mixins from 'vue-typed-mixins';
Expand Down Expand Up @@ -215,7 +213,7 @@ export const pushConnection = mixins(

const runDataExecuted = pushData.data;

const runDataExecutedErrorMessage = this.$getExecutionError(runDataExecuted.data.resultData.error);
const runDataExecutedErrorMessage = this.$getExecutionError(runDataExecuted.data);

const workflow = this.getCurrentWorkflow();
if (runDataExecuted.waitTill !== undefined) {
Expand Down Expand Up @@ -251,8 +249,48 @@ export const pushConnection = mixins(
} else if (runDataExecuted.finished !== true) {
this.$titleSet(workflow.name as string, 'ERROR');

if (
runDataExecuted.data.resultData.error!.name === 'ExpressionError' &&
(runDataExecuted.data.resultData.error as ExpressionError).context.functionality === 'pairedItem'
) {
const error = runDataExecuted.data.resultData.error as ExpressionError;

this.getWorkflowDataToSave().then((workflowData) => {
const eventData: IDataObject = {
caused_by_credential: false,
error_message: error.description,
error_title: error.message,
error_type: error.context.type,
node_graph_string: JSON.stringify(TelemetryHelpers.generateNodesGraph(workflowData as IWorkflowBase, this.getNodeTypes()).nodeGraph),
workflow_id: this.$store.getters.workflowId,
};

if (error.context.nodeCause && ['no pairing info', 'invalid pairing info'].includes(error.context.type as string)) {
const node = workflow.getNode(error.context.nodeCause as string);

if (node) {
eventData.is_pinned = !!workflow.getPinDataOfNode(node.name);
eventData.mode = node.parameters.mode;
eventData.node_type = node.type;
eventData.operation = node.parameters.operation;
eventData.resource = node.parameters.resource;
}
}

this.$telemetry.track('Instance FE emitted paired item error', eventData);
});

}

let title: string;
if (runDataExecuted.data.resultData.lastNodeExecuted) {
title = `Problem in node ‘${runDataExecuted.data.resultData.lastNodeExecuted}‘`;
} else {
title = 'Problem executing workflow';
}

this.$showMessage({
title: 'Problem executing workflow',
title,
message: runDataExecutedErrorMessage,
type: 'error',
duration: 0,
Expand Down
Loading