import WorkflowValidator from '../resources/WorkflowValidator.js';
/**
* Validateur pour la gestion des erreurs dans les workflows n8n
*/
class ErrorHandlingValidator implements WorkflowValidator {
/**
* Valide la gestion des erreurs dans un workflow n8n
* @param workflow Les données du workflow à valider
* @param strictness Le niveau de rigueur de la validation
* @returns Résultat de la validation avec les problèmes détectés
*/
validate(workflow: any, strictness: 'low' | 'medium' | 'high') {
const issues: Array<{
message: string;
recommendation: string;
severity: 'low' | 'medium' | 'high';
location?: string;
}> = [];
// Vérifier si le workflow a une configuration globale de gestion d'erreurs
if (!workflow.settings?.errorWorkflow) {
issues.push({
message: 'Aucun workflow d\'erreur global n\'est configuré',
recommendation: 'Configurez un workflow d\'erreur global pour gérer les échecs',
severity: strictness === 'low' ? 'medium' : 'high',
});
}
// Vérifier les nœuds pour la gestion d'erreurs
if (workflow.nodes && Array.isArray(workflow.nodes)) {
// Identifier les nœuds HTTP, API ou d'intégration externe
const externalNodes = workflow.nodes.filter((node: any) => {
const type = (node.type || '').toLowerCase();
return type.includes('http') ||
type.includes('api') ||
type.includes('webhook') ||
type.includes('ftp') ||
type.includes('database') ||
type.includes('email');
});
// Vérifier si ces nœuds ont une gestion d'erreurs
externalNodes.forEach((node: any) => {
// Vérifier si le nœud a une configuration de gestion d'erreurs
const hasErrorHandling = this.nodeHasErrorHandling(node, workflow);
if (!hasErrorHandling) {
issues.push({
message: `Le nœud "${node.name}" n'a pas de gestion d'erreurs`,
recommendation: 'Ajoutez une gestion d\'erreurs pour ce nœud en utilisant des nœuds Error Trigger ou en configurant des chemins d\'erreur',
severity: 'high',
location: node.id,
});
}
});
// Vérifier les connexions entre les nœuds pour détecter les chemins d'erreur manquants
if (workflow.connections && strictness !== 'low') {
const nodesWithErrorOutputs = this.getNodesWithErrorOutputs(workflow);
workflow.nodes.forEach((node: any) => {
// Ignorer les nœuds de déclenchement (triggers)
if ((node.type || '').toLowerCase().includes('trigger')) {
return;
}
// Vérifier si le nœud a une sortie d'erreur configurée
if (!nodesWithErrorOutputs.includes(node.id)) {
issues.push({
message: `Le nœud "${node.name}" n'a pas de chemin d'erreur configuré`,
recommendation: 'Ajoutez un chemin d\'erreur pour gérer les échecs potentiels',
severity: strictness === 'medium' ? 'low' : 'medium',
location: node.id,
});
}
});
}
}
// Vérifier si le workflow contient des nœuds spécifiques à la gestion d'erreurs
const hasErrorHandlingNodes = this.workflowHasErrorHandlingNodes(workflow);
if (!hasErrorHandlingNodes && strictness !== 'low') {
issues.push({
message: 'Le workflow ne contient pas de nœuds dédiés à la gestion d\'erreurs',
recommendation: 'Ajoutez des nœuds Error Trigger ou Error Catcher pour gérer les erreurs de manière centralisée',
severity: 'medium',
});
}
return {
valid: issues.length === 0,
issues,
};
}
/**
* Vérifie si un nœud a une gestion d'erreurs configurée
*/
private nodeHasErrorHandling(node: any, workflow: any): boolean {
// Vérifier si le nœud a une configuration d'erreur spécifique
if (node.continueOnFail === true) {
return true;
}
// Vérifier si le nœud est connecté à un gestionnaire d'erreurs
if (workflow.connections) {
// Chercher des connexions sortantes depuis ce nœud vers un nœud de gestion d'erreurs
const nodeConnections = workflow.connections[node.id];
if (nodeConnections) {
// Vérifier les connexions de type "error"
return Object.values(nodeConnections).some((connections: any) => {
return connections.some((connection: any) => connection.type === 'error');
});
}
}
return false;
}
/**
* Obtient la liste des IDs de nœuds qui ont des sorties d'erreur configurées
*/
private getNodesWithErrorOutputs(workflow: any): string[] {
const nodesWithErrorOutputs: string[] = [];
if (workflow.connections) {
// Parcourir toutes les connexions pour trouver celles de type "error"
Object.entries(workflow.connections).forEach(([sourceNodeId, connections]: [string, any]) => {
Object.values(connections).forEach((targetConnections: any) => {
if (targetConnections.some((connection: any) => connection.type === 'error')) {
nodesWithErrorOutputs.push(sourceNodeId);
}
});
});
}
return nodesWithErrorOutputs;
}
/**
* Vérifie si le workflow contient des nœuds spécifiques à la gestion d'erreurs
*/
private workflowHasErrorHandlingNodes(workflow: any): boolean {
if (!workflow.nodes || !Array.isArray(workflow.nodes)) {
return false;
}
return workflow.nodes.some((node: any) => {
const type = (node.type || '').toLowerCase();
return type.includes('error') ||
type.includes('catch') ||
type.includes('try') ||
type.includes('exception');
});
}
}
export default ErrorHandlingValidator;