🐛 Swallow telemetry error and only log in warn and debug mode (#2858)

* catch nodegraph errors

* use loglevel config for telemetry

* Use getByNameAndVersion instead of getByName

* remove any usage of nodeTypes.getByName method

* deprecate getByName method
This commit is contained in:
Ahsan Virani 2022-02-24 17:15:30 +01:00 committed by GitHub
parent 2b9f3aab1b
commit 8fc1095d1e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 58 additions and 74 deletions

View file

@ -54,13 +54,6 @@ const mockNodeTypes: INodeTypes = {
// @ts-ignore // @ts-ignore
return Object.values(this.nodeTypes).map((data) => data.type); return Object.values(this.nodeTypes).map((data) => data.type);
}, },
// eslint-disable-next-line @typescript-eslint/no-unused-vars
getByName(nodeType: string): INodeType | INodeVersionedType | undefined {
if (this.nodeTypes[nodeType] === undefined) {
return undefined;
}
return this.nodeTypes[nodeType].type;
},
getByNameAndVersion(nodeType: string, version?: number): INodeType | undefined { getByNameAndVersion(nodeType: string, version?: number): INodeType | undefined {
if (this.nodeTypes[nodeType] === undefined) { if (this.nodeTypes[nodeType] === undefined) {
return undefined; return undefined;
@ -524,7 +517,7 @@ export class CredentialsHelper extends ICredentialsHelper {
nodeType = credentialTestFunction.nodeType; nodeType = credentialTestFunction.nodeType;
} else { } else {
const nodeTypes = NodeTypes(); const nodeTypes = NodeTypes();
nodeType = nodeTypes.getByName('n8n-nodes-base.noOp') as INodeType; nodeType = nodeTypes.getByNameAndVersion('n8n-nodes-base.noOp');
} }
const node: INode = { const node: INode = {

View file

@ -33,13 +33,6 @@ class NodeTypesClass implements INodeTypes {
return Object.values(this.nodeTypes).map((data) => data.type); return Object.values(this.nodeTypes).map((data) => data.type);
} }
getByName(nodeType: string): INodeType | INodeVersionedType | undefined {
if (this.nodeTypes[nodeType] === undefined) {
throw new Error(`The node-type "${nodeType}" is not known!`);
}
return this.nodeTypes[nodeType].type;
}
/** /**
* Variant of `getByNameAndVersion` that includes the node's source path, used to locate a node's translations. * Variant of `getByNameAndVersion` that includes the node's source path, used to locate a node's translations.
*/ */

View file

@ -58,6 +58,7 @@ export class Telemetry {
this.versionCli = versionCli; this.versionCli = versionCli;
const enabled = config.get('diagnostics.enabled') as boolean; const enabled = config.get('diagnostics.enabled') as boolean;
const logLevel = config.get('logs.level') as boolean;
if (enabled) { if (enabled) {
const conf = config.get('diagnostics.config.backend') as string; const conf = config.get('diagnostics.config.backend') as string;
const [key, url] = conf.split(';'); const [key, url] = conf.split(';');
@ -69,7 +70,7 @@ export class Telemetry {
return; return;
} }
this.client = new TelemetryClient(key, url); this.client = new TelemetryClient(key, url, { logLevel });
this.pulseIntervalReference = setInterval(async () => { this.pulseIntervalReference = setInterval(async () => {
void this.pulse(); void this.pulse();

View file

@ -24,7 +24,6 @@ import {
IRunExecutionData, IRunExecutionData,
IWorfklowIssues, IWorfklowIssues,
IWorkflowDataProxyAdditionalKeys, IWorkflowDataProxyAdditionalKeys,
TelemetryHelpers,
Workflow, Workflow,
NodeHelpers, NodeHelpers,
} from 'n8n-workflow'; } from 'n8n-workflow';
@ -224,17 +223,6 @@ export const workflowHelpers = mixins(
// Does not get used in Workflow so no need to return it // Does not get used in Workflow so no need to return it
return []; return [];
}, },
getByName: (nodeType: string): INodeType | INodeVersionedType | undefined => {
const nodeTypeDescription = this.$store.getters.nodeType(nodeType) as INodeTypeDescription | null;
if (nodeTypeDescription === null) {
return undefined;
}
return {
description: nodeTypeDescription,
};
},
getByNameAndVersion: (nodeType: string, version?: number): INodeType | undefined => { getByNameAndVersion: (nodeType: string, version?: number): INodeType | undefined => {
const nodeTypeDescription = this.$store.getters.nodeType(nodeType, version) as INodeTypeDescription | null; const nodeTypeDescription = this.$store.getters.nodeType(nodeType, version) as INodeTypeDescription | null;

View file

@ -1193,7 +1193,6 @@ export interface INodeTypes {
nodeTypes: INodeTypeData; nodeTypes: INodeTypeData;
init(nodeTypes?: INodeTypeData): Promise<void>; init(nodeTypes?: INodeTypeData): Promise<void>;
getAll(): Array<INodeType | INodeVersionedType>; getAll(): Array<INodeType | INodeVersionedType>;
getByName(nodeType: string): INodeType | INodeVersionedType | undefined;
getByNameAndVersion(nodeType: string, version?: number): INodeType | undefined; getByNameAndVersion(nodeType: string, version?: number): INodeType | undefined;
} }

View file

@ -8,9 +8,10 @@ import {
INodesGraphResult, INodesGraphResult,
IWorkflowBase, IWorkflowBase,
INodeTypes, INodeTypes,
INodeType,
} from '.'; } from '.';
import { getInstance as getLoggerInstance } from './LoggerProxy';
export function getNodeTypeForName(workflow: IWorkflowBase, nodeName: string): INode | undefined { export function getNodeTypeForName(workflow: IWorkflowBase, nodeName: string): INode | undefined {
return workflow.nodes.find((node) => node.name === nodeName); return workflow.nodes.find((node) => node.name === nodeName);
} }
@ -26,51 +27,59 @@ export function generateNodesGraph(
}; };
const nodeNameAndIndex: INodeNameIndex = {}; const nodeNameAndIndex: INodeNameIndex = {};
workflow.nodes.forEach((node: INode, index: number) => { try {
nodesGraph.node_types.push(node.type); workflow.nodes.forEach((node: INode, index: number) => {
const nodeItem: INodeGraphItem = { nodesGraph.node_types.push(node.type);
type: node.type, const nodeItem: INodeGraphItem = {
position: node.position, type: node.type,
position: node.position,
};
if (node.type === 'n8n-nodes-base.httpRequest') {
try {
nodeItem.domain = new URL(node.parameters.url as string).hostname;
} catch (e) {
nodeItem.domain = node.parameters.url as string;
}
} else {
const nodeType = nodeTypes.getByNameAndVersion(node.type);
nodeType?.description.properties.forEach((property) => {
if (
property.name === 'operation' ||
property.name === 'resource' ||
property.name === 'mode'
) {
nodeItem[property.name] = property.default ? property.default.toString() : undefined;
}
});
nodeItem.operation = node.parameters.operation?.toString() ?? nodeItem.operation;
nodeItem.resource = node.parameters.resource?.toString() ?? nodeItem.resource;
nodeItem.mode = node.parameters.mode?.toString() ?? nodeItem.mode;
}
nodesGraph.nodes[`${index}`] = nodeItem;
nodeNameAndIndex[node.name] = index.toString();
});
const getGraphConnectionItem = (startNode: string, connectionItem: IConnection) => {
return { start: nodeNameAndIndex[startNode], end: nodeNameAndIndex[connectionItem.node] };
}; };
if (node.type === 'n8n-nodes-base.httpRequest') { Object.keys(workflow.connections).forEach((nodeName) => {
try { const connections = workflow.connections[nodeName];
nodeItem.domain = new URL(node.parameters.url as string).hostname; connections.main.forEach((element) => {
} catch (e) { element.forEach((element2) => {
nodeItem.domain = node.parameters.url as string; nodesGraph.node_connections.push(getGraphConnectionItem(nodeName, element2));
} });
} else {
const nodeType = nodeTypes.getByName(node.type) as INodeType;
nodeType.description.properties.forEach((property) => {
if (
property.name === 'operation' ||
property.name === 'resource' ||
property.name === 'mode'
) {
nodeItem[property.name] = property.default ? property.default.toString() : undefined;
}
});
nodeItem.operation = node.parameters.operation?.toString() ?? nodeItem.operation;
nodeItem.resource = node.parameters.resource?.toString() ?? nodeItem.resource;
nodeItem.mode = node.parameters.mode?.toString() ?? nodeItem.mode;
}
nodesGraph.nodes[`${index}`] = nodeItem;
nodeNameAndIndex[node.name] = index.toString();
});
const getGraphConnectionItem = (startNode: string, connectionItem: IConnection) => {
return { start: nodeNameAndIndex[startNode], end: nodeNameAndIndex[connectionItem.node] };
};
Object.keys(workflow.connections).forEach((nodeName) => {
const connections = workflow.connections[nodeName];
connections.main.forEach((element) => {
element.forEach((element2) => {
nodesGraph.node_connections.push(getGraphConnectionItem(nodeName, element2));
}); });
}); });
}); } catch (e) {
const logger = getLoggerInstance();
logger.warn(`Failed to generate nodes graph for workflowId: ${workflow.id as string | number}`);
logger.warn((e as Error).message);
logger.warn((e as Error).stack ?? '');
}
return { nodeGraph: nodesGraph, nameIndices: nodeNameAndIndex }; return { nodeGraph: nodesGraph, nameIndices: nodeNameAndIndex };
} }

View file

@ -23,6 +23,7 @@ import {
INodeType, INodeType,
INodeTypeData, INodeTypeData,
INodeTypes, INodeTypes,
INodeVersionedType,
IRunExecutionData, IRunExecutionData,
ITaskDataConnections, ITaskDataConnections,
IWorkflowBase, IWorkflowBase,
@ -614,7 +615,7 @@ class NodeTypesClass implements INodeTypes {
return Object.values(this.nodeTypes).map((data) => NodeHelpers.getVersionedNodeType(data.type)); return Object.values(this.nodeTypes).map((data) => NodeHelpers.getVersionedNodeType(data.type));
} }
getByName(nodeType: string): INodeType { getByName(nodeType: string): INodeType | INodeVersionedType | undefined {
return this.getByNameAndVersion(nodeType); return this.getByNameAndVersion(nodeType);
} }

View file

@ -618,7 +618,7 @@ describe('RoutingNode', () => {
const runExecutionData: IRunExecutionData = { resultData: { runData: {} } }; const runExecutionData: IRunExecutionData = { resultData: { runData: {} } };
const additionalData = Helpers.WorkflowExecuteAdditionalData(); const additionalData = Helpers.WorkflowExecuteAdditionalData();
const path = ''; const path = '';
const nodeType = nodeTypes.getByName(node.type); const nodeType = nodeTypes.getByNameAndVersion(node.type);
const workflowData = { const workflowData = {
nodes: [node], nodes: [node],
@ -1596,7 +1596,7 @@ describe('RoutingNode', () => {
const connectionInputData: INodeExecutionData[] = []; const connectionInputData: INodeExecutionData[] = [];
const runExecutionData: IRunExecutionData = { resultData: { runData: {} } }; const runExecutionData: IRunExecutionData = { resultData: { runData: {} } };
const additionalData = Helpers.WorkflowExecuteAdditionalData(); const additionalData = Helpers.WorkflowExecuteAdditionalData();
const nodeType = nodeTypes.getByName(baseNode.type); const nodeType = nodeTypes.getByNameAndVersion(baseNode.type);
const inputData: ITaskDataConnections = { const inputData: ITaskDataConnections = {
main: [ main: [