#!/usr/bin/env python3 """ Script d'Analyse - Cas Undefined stepParametersConfig Auteur : Dom, Alice, Kiro - 12 janvier 2026 Ce script identifie tous les cas où stepParametersConfig[selectedStep.type] retourne undefined pour diagnostiquer les problèmes de propriétés d'étapes vides. """ import json import os import sys import re import time from pathlib import Path from typing import Dict, List, Any, Optional, Set, Tuple class AnalyseCasUndefinedStepParametersConfig: """Analyse des cas undefined dans stepParametersConfig.""" def __init__(self): """Initialise l'analyse.""" self.project_root = Path(__file__).parent.parent self.frontend_path = self.project_root / "visual_workflow_builder" / "frontend" self.analysis_results = { "timestamp": "2026-01-12", "version": "1.0.0", "analysis_type": "cas_undefined_stepparametersconfig", "summary": { "total_step_types": 0, "configured_types": 0, "undefined_cases": 0, "vwb_actions_analyzed": 0, "potential_issues": 0 }, "step_types_analysis": { "configured_types": [], "undefined_types": [], "type_mapping": {} }, "vwb_actions_analysis": { "known_vwb_actions": [], "catalog_actions": [], "unmapped_actions": [] }, "undefined_scenarios": [], "recommendations": [], "detailed_findings": [] } print("🔍 Analyse des Cas Undefined - stepParametersConfig") print(f"📁 Chemin frontend: {self.frontend_path}") def run_complete_analysis(self) -> Dict[str, Any]: """Exécute l'analyse complète.""" try: print("\n" + "="*70) print("🚀 ANALYSE COMPLÈTE DES CAS UNDEFINED") print("="*70) # 1. Analyser la configuration stepParametersConfig self._analyze_step_parameters_config() # 2. Analyser les types TypeScript self._analyze_typescript_step_types() # 3. Analyser les actions VWB du catalogue self._analyze_vwb_catalog_actions() # 4. Identifier les scénarios undefined self._identify_undefined_scenarios() # 5. Analyser les cas d'usage réels self._analyze_real_usage_cases() # 6. Générer les recommandations self._generate_recommendations() # 7. Sauvegarder le rapport self._save_analysis_report() print(f"\n✅ Analyse terminée - {self.analysis_results['summary']['undefined_cases']} cas undefined identifiés") return self.analysis_results except Exception as e: print(f"❌ Erreur lors de l'analyse : {e}") self.analysis_results["fatal_error"] = str(e) return self.analysis_results def _analyze_step_parameters_config(self): """Analyse la configuration stepParametersConfig.""" print("\n📋 Analyse de stepParametersConfig...") try: # Lire le fichier PropertiesPanel properties_panel_path = self.frontend_path / "src" / "components" / "PropertiesPanel" / "index.tsx" if not properties_panel_path.exists(): self._add_finding("CRITICAL", "Fichier PropertiesPanel introuvable", {}) return content = properties_panel_path.read_text(encoding='utf-8') # Extraire la configuration stepParametersConfig config_match = re.search( r'const stepParametersConfig: Record = \{(.*?)\};', content, re.DOTALL ) if not config_match: self._add_finding("CRITICAL", "Configuration stepParametersConfig introuvable", {}) return config_content = config_match.group(1) # Extraire les types configurés configured_types = re.findall(r'(\w+):\s*\[', config_content) # Analyser chaque configuration type_mapping = {} for step_type in configured_types: # Extraire la configuration détaillée pour ce type type_pattern = rf'{step_type}:\s*\[(.*?)\],' type_match = re.search(type_pattern, config_content, re.DOTALL) if type_match: type_config = type_match.group(1) param_count = len(re.findall(r'\{[^}]+\}', type_config)) type_mapping[step_type] = { "parameter_count": param_count, "has_configuration": True, "config_content": type_config.strip() } else: type_mapping[step_type] = { "parameter_count": 0, "has_configuration": False, "config_content": "" } self.analysis_results["step_types_analysis"] = { "configured_types": configured_types, "type_mapping": type_mapping, "total_configured": len(configured_types) } self.analysis_results["summary"]["configured_types"] = len(configured_types) print(f" ✅ {len(configured_types)} types configurés trouvés") for step_type in configured_types: param_count = type_mapping[step_type]["parameter_count"] print(f" - {step_type}: {param_count} paramètres") except Exception as e: self._add_finding("ERROR", f"Erreur analyse stepParametersConfig: {e}", {}) def _analyze_typescript_step_types(self): """Analyse les types TypeScript StepType.""" print("\n🔧 Analyse des types TypeScript...") try: # Lire le fichier des types types_file_path = self.frontend_path / "src" / "types" / "index.ts" if not types_file_path.exists(): self._add_finding("HIGH", "Fichier types TypeScript introuvable", {}) return types_content = types_file_path.read_text(encoding='utf-8') # Extraire l'union StepType step_type_match = re.search( r'export type StepType = ([^;]+);', types_content ) typescript_types = set() if step_type_match: step_type_union = step_type_match.group(1) typescript_types = set(re.findall(r"'(\w+)'", step_type_union)) # Comparer avec les types configurés configured_types = set(self.analysis_results["step_types_analysis"]["configured_types"]) # Identifier les types undefined (présents en TypeScript mais pas configurés) undefined_types = list(typescript_types - configured_types) extra_configured = list(configured_types - typescript_types) self.analysis_results["step_types_analysis"]["typescript_types"] = list(typescript_types) self.analysis_results["step_types_analysis"]["undefined_types"] = undefined_types self.analysis_results["step_types_analysis"]["extra_configured"] = extra_configured self.analysis_results["summary"]["total_step_types"] = len(typescript_types) self.analysis_results["summary"]["undefined_cases"] = len(undefined_types) # Signaler les cas undefined if undefined_types: self._add_finding("HIGH", f"Types TypeScript sans configuration: {undefined_types}", { "undefined_types": undefined_types, "impact": "Ces types retourneront undefined dans stepParametersConfig", "consequence": "Propriétés d'étapes vides pour ces types" }) if extra_configured: self._add_finding("MEDIUM", f"Configurations sans type TypeScript: {extra_configured}", { "extra_configured": extra_configured, "impact": "Configurations inutilisées" }) print(f" ✅ {len(typescript_types)} types TypeScript trouvés") print(f" ✅ {len(configured_types)} types configurés") print(f" ⚠️ {len(undefined_types)} types undefined identifiés: {undefined_types}") except Exception as e: self._add_finding("ERROR", f"Erreur analyse types TypeScript: {e}", {}) def _analyze_vwb_catalog_actions(self): """Analyse les actions VWB du catalogue.""" print("\n🎯 Analyse des actions VWB du catalogue...") try: # Analyser le catalogue statique static_catalog_path = self.frontend_path / "src" / "data" / "staticCatalog.ts" catalog_actions = [] if static_catalog_path.exists(): catalog_content = static_catalog_path.read_text(encoding='utf-8') # Extraire les actions du catalogue actions_pattern = r"id:\s*['\"]([^'\"]+)['\"]" catalog_actions = re.findall(actions_pattern, catalog_content) # Actions VWB connues dans le code properties_panel_path = self.frontend_path / "src" / "components" / "PropertiesPanel" / "index.tsx" known_vwb_actions = [] if properties_panel_path.exists(): content = properties_panel_path.read_text(encoding='utf-8') # Extraire la liste des actions VWB connues known_actions_match = re.search( r'knownVWBActions = \[(.*?)\]', content, re.DOTALL ) if known_actions_match: actions_content = known_actions_match.group(1) known_vwb_actions = re.findall(r"'([^']+)'", actions_content) # Identifier les actions non mappées all_vwb_actions = set(catalog_actions + known_vwb_actions) configured_types = set(self.analysis_results["step_types_analysis"]["configured_types"]) unmapped_vwb_actions = list(all_vwb_actions - configured_types) self.analysis_results["vwb_actions_analysis"] = { "catalog_actions": catalog_actions, "known_vwb_actions": known_vwb_actions, "all_vwb_actions": list(all_vwb_actions), "unmapped_actions": unmapped_vwb_actions } self.analysis_results["summary"]["vwb_actions_analyzed"] = len(all_vwb_actions) # Signaler les actions VWB non mappées if unmapped_vwb_actions: self._add_finding("MEDIUM", f"Actions VWB non mappées dans stepParametersConfig: {unmapped_vwb_actions}", { "unmapped_actions": unmapped_vwb_actions, "impact": "Ces actions VWB utiliseront le composant VWBActionProperties", "note": "Comportement attendu pour les actions VWB" }) print(f" ✅ {len(catalog_actions)} actions du catalogue analysées") print(f" ✅ {len(known_vwb_actions)} actions VWB connues") print(f" ✅ {len(unmapped_vwb_actions)} actions VWB non mappées (normal)") except Exception as e: self._add_finding("ERROR", f"Erreur analyse actions VWB: {e}", {}) def _identify_undefined_scenarios(self): """Identifie les scénarios spécifiques qui causent undefined.""" print("\n🔍 Identification des scénarios undefined...") try: undefined_scenarios = [] # Scénario 1: Types TypeScript sans configuration undefined_types = self.analysis_results["step_types_analysis"].get("undefined_types", []) for step_type in undefined_types: scenario = { "scenario_type": "typescript_without_config", "step_type": step_type, "description": f"Type '{step_type}' défini en TypeScript mais absent de stepParametersConfig", "consequence": "stepParametersConfig[selectedStep.type] retourne undefined", "user_impact": "Affichage 'Cette étape n'a pas de paramètres configurables'", "severity": "HIGH", "fix_required": True } undefined_scenarios.append(scenario) # Scénario 2: Types dynamiques non prévus dynamic_types = [ "custom_action", "user_defined", "plugin_action", "external_tool", "api_call", "database_query" ] for dynamic_type in dynamic_types: scenario = { "scenario_type": "dynamic_type_not_configured", "step_type": dynamic_type, "description": f"Type dynamique '{dynamic_type}' potentiellement créé à l'exécution", "consequence": "stepParametersConfig[selectedStep.type] retourne undefined", "user_impact": "Propriétés non configurables pour les types dynamiques", "severity": "MEDIUM", "fix_required": False, "note": "Peut nécessiter une gestion spéciale" } undefined_scenarios.append(scenario) # Scénario 3: Actions VWB mal détectées vwb_actions = self.analysis_results["vwb_actions_analysis"].get("all_vwb_actions", []) for vwb_action in vwb_actions: scenario = { "scenario_type": "vwb_action_detection_failure", "step_type": vwb_action, "description": f"Action VWB '{vwb_action}' non détectée par la logique VWB", "consequence": "Utilisation de stepParametersConfig au lieu de VWBActionProperties", "user_impact": "Propriétés VWB non affichées correctement", "severity": "MEDIUM", "fix_required": True, "note": "Améliorer la détection VWB" } undefined_scenarios.append(scenario) self.analysis_results["undefined_scenarios"] = undefined_scenarios self.analysis_results["summary"]["potential_issues"] = len(undefined_scenarios) print(f" ✅ {len(undefined_scenarios)} scénarios undefined identifiés") # Afficher les scénarios critiques critical_scenarios = [s for s in undefined_scenarios if s["severity"] == "HIGH"] if critical_scenarios: print(f" 🚨 {len(critical_scenarios)} scénarios critiques:") for scenario in critical_scenarios: print(f" - {scenario['step_type']}: {scenario['description']}") except Exception as e: self._add_finding("ERROR", f"Erreur identification scénarios: {e}", {}) def _analyze_real_usage_cases(self): """Analyse les cas d'usage réels dans le code.""" print("\n📊 Analyse des cas d'usage réels...") try: # Analyser l'utilisation de stepParametersConfig dans PropertiesPanel properties_panel_path = self.frontend_path / "src" / "components" / "PropertiesPanel" / "index.tsx" content = properties_panel_path.read_text(encoding='utf-8') # Rechercher les accès à stepParametersConfig config_accesses = re.findall( r'stepParametersConfig\[([^\]]+)\]', content ) # Analyser la fonction getParameterConfig get_param_config_match = re.search( r'const getParameterConfig = useCallback\(\(\): ParameterConfig\[\] => \{(.*?)\}, \[selectedStep\]\);', content, re.DOTALL ) usage_analysis = { "config_accesses": config_accesses, "access_count": len(config_accesses), "has_fallback_logic": False, "has_undefined_check": False, "has_error_handling": False } if get_param_config_match: config_logic = get_param_config_match.group(1) # Vérifier la présence de logiques de protection usage_analysis["has_fallback_logic"] = "|| []" in config_logic usage_analysis["has_undefined_check"] = "undefined" in config_logic usage_analysis["has_error_handling"] = "try" in config_logic and "catch" in config_logic # Analyser les conditions de retour return_statements = re.findall(r'return ([^;]+);', config_logic) usage_analysis["return_statements"] = return_statements self.analysis_results["real_usage_analysis"] = usage_analysis # Signaler les problèmes d'usage if not usage_analysis["has_fallback_logic"]: self._add_finding("MEDIUM", "Pas de logique de fallback pour stepParametersConfig", { "suggestion": "Ajouter '|| []' pour éviter les erreurs undefined" }) if not usage_analysis["has_error_handling"]: self._add_finding("LOW", "Pas de gestion d'erreurs dans getParameterConfig", { "suggestion": "Ajouter try/catch pour la robustesse" }) print(f" ✅ {usage_analysis['access_count']} accès à stepParametersConfig analysés") print(f" ✅ Fallback logic: {'Oui' if usage_analysis['has_fallback_logic'] else 'Non'}") print(f" ✅ Gestion d'erreurs: {'Oui' if usage_analysis['has_error_handling'] else 'Non'}") except Exception as e: self._add_finding("ERROR", f"Erreur analyse usage réel: {e}", {}) def _generate_recommendations(self): """Génère des recommandations pour résoudre les cas undefined.""" print("\n💡 Génération des recommandations...") recommendations = [] # Recommandation 1: Corriger les types undefined undefined_types = self.analysis_results["step_types_analysis"].get("undefined_types", []) if undefined_types: recommendations.append({ "priority": "HIGH", "category": "Configuration manquante", "title": "Ajouter les configurations manquantes pour les types undefined", "description": f"{len(undefined_types)} types retournent undefined", "affected_types": undefined_types, "actions": [ f"Ajouter une configuration pour chaque type: {', '.join(undefined_types)}", "Définir les paramètres appropriés pour chaque type", "Tester l'affichage des propriétés après ajout" ], "code_example": self._generate_config_example(undefined_types[0] if undefined_types else "example") }) # Recommandation 2: Améliorer la robustesse usage_analysis = self.analysis_results.get("real_usage_analysis", {}) if not usage_analysis.get("has_fallback_logic", False): recommendations.append({ "priority": "MEDIUM", "category": "Robustesse", "title": "Ajouter une logique de fallback pour éviter undefined", "description": "Protéger contre les accès undefined à stepParametersConfig", "actions": [ "Ajouter '|| []' après stepParametersConfig[selectedStep.type]", "Implémenter une configuration par défaut", "Ajouter des logs pour diagnostiquer les cas undefined" ], "code_example": "const config = stepParametersConfig[selectedStep.type as StepType] || [];" }) # Recommandation 3: Améliorer la détection VWB vwb_actions = self.analysis_results["vwb_actions_analysis"].get("unmapped_actions", []) if len(vwb_actions) > 5: # Si beaucoup d'actions VWB non mappées recommendations.append({ "priority": "MEDIUM", "category": "Détection VWB", "title": "Renforcer la détection des actions VWB", "description": f"{len(vwb_actions)} actions VWB pourraient être mal détectées", "actions": [ "Améliorer les méthodes de détection VWB", "Ajouter plus de patterns de reconnaissance", "Tester la détection avec toutes les actions du catalogue" ] }) # Recommandation 4: Documentation recommendations.append({ "priority": "LOW", "category": "Documentation", "title": "Documenter les cas undefined et leur résolution", "description": "Créer une documentation pour les développeurs", "actions": [ "Documenter tous les types d'étapes supportés", "Expliquer la différence entre types standard et VWB", "Créer un guide de débogage pour les propriétés vides" ] }) self.analysis_results["recommendations"] = recommendations print(f" ✅ {len(recommendations)} recommandations générées") def _generate_config_example(self, step_type: str) -> str: """Génère un exemple de configuration pour un type d'étape.""" return f""" {step_type}: [ {{ name: 'parameter1', label: 'Paramètre 1', type: 'text', required: true, description: 'Description du paramètre' }}, {{ name: 'parameter2', label: 'Paramètre 2', type: 'boolean', defaultValue: false }} ],""" def _add_finding(self, severity: str, description: str, details: Dict[str, Any]): """Ajoute un résultat d'analyse.""" finding = { "severity": severity, "description": description, "details": details, "timestamp": time.time() } self.analysis_results["detailed_findings"].append(finding) def _save_analysis_report(self): """Sauvegarde le rapport d'analyse.""" try: # Créer le répertoire docs s'il n'existe pas docs_path = self.project_root / "docs" docs_path.mkdir(exist_ok=True) # Sauvegarder le rapport JSON json_report_path = docs_path / "ANALYSE_CAS_UNDEFINED_STEPPARAMETERSCONFIG_12JAN2026.json" with open(json_report_path, 'w', encoding='utf-8') as f: json.dump(self.analysis_results, f, indent=2, ensure_ascii=False) # Créer un résumé markdown md_report_path = docs_path / "ANALYSE_CAS_UNDEFINED_STEPPARAMETERSCONFIG_12JAN2026.md" self._create_markdown_summary(md_report_path) print(f"\n📄 Rapport JSON sauvegardé : {json_report_path}") print(f"📄 Résumé Markdown créé : {md_report_path}") except Exception as e: print(f"❌ Erreur sauvegarde rapport : {e}") def _create_markdown_summary(self, output_path: Path): """Crée un résumé markdown de l'analyse.""" summary = self.analysis_results["summary"] markdown_content = f"""# Analyse des Cas Undefined - stepParametersConfig **Auteur :** Dom, Alice, Kiro **Date :** 12 janvier 2026 **Version :** 1.0.0 ## Résumé Exécutif - **Types d'étapes totaux :** {summary['total_step_types']} - **Types configurés :** {summary['configured_types']} - **Cas undefined :** {summary['undefined_cases']} - **Actions VWB analysées :** {summary['vwb_actions_analyzed']} - **Problèmes potentiels :** {summary['potential_issues']} ## Types Undefined Identifiés """ undefined_types = self.analysis_results["step_types_analysis"].get("undefined_types", []) if undefined_types: markdown_content += "Les types suivants retournent `undefined` dans `stepParametersConfig`:\n\n" for step_type in undefined_types: markdown_content += f"- `{step_type}` - Défini en TypeScript mais pas configuré\n" else: markdown_content += "✅ Aucun type undefined identifié.\n" # Ajouter les recommandations recommendations = self.analysis_results.get("recommendations", []) if recommendations: markdown_content += "\n## Recommandations\n\n" for i, rec in enumerate(recommendations, 1): markdown_content += f"### {i}. {rec['title']} ({rec['priority']})\n\n" markdown_content += f"**Catégorie :** {rec['category']} \n" markdown_content += f"**Description :** {rec['description']}\n\n" if 'actions' in rec: markdown_content += "**Actions :**\n" for action in rec['actions']: markdown_content += f"- {action}\n" if 'code_example' in rec: markdown_content += f"\n**Exemple de code :**\n```typescript\n{rec['code_example']}\n```\n" markdown_content += "\n" # Sauvegarder le markdown with open(output_path, 'w', encoding='utf-8') as f: f.write(markdown_content) def main(): """Fonction principale.""" print("🔍 Analyse des Cas Undefined - stepParametersConfig") analyzer = AnalyseCasUndefinedStepParametersConfig() results = analyzer.run_complete_analysis() # Afficher le résumé final print("\n" + "="*70) print("📊 RÉSUMÉ DE L'ANALYSE DES CAS UNDEFINED") print("="*70) summary = results['summary'] print(f"✅ Analyse terminée avec succès") print(f"📊 Types d'étapes totaux : {summary['total_step_types']}") print(f"✅ Types configurés : {summary['configured_types']}") print(f"⚠️ Cas undefined : {summary['undefined_cases']}") print(f"🎯 Actions VWB analysées : {summary['vwb_actions_analyzed']}") undefined_types = results["step_types_analysis"].get("undefined_types", []) if undefined_types: print(f"\n🚨 Types undefined identifiés :") for step_type in undefined_types: print(f" - {step_type}") else: print(f"\n🎉 Aucun type undefined critique identifié !") recommendations = results.get("recommendations", []) if recommendations: print(f"\n💡 Recommandations : {len(recommendations)}") for rec in recommendations: print(f" - {rec['priority']}: {rec['title']}") print(f"\n📄 Rapport complet disponible dans docs/") # Code de sortie basé sur les cas undefined critiques if summary['undefined_cases'] > 0: print("⚠️ Cas undefined détectés - correction recommandée") return 1 else: print("🎉 Aucun cas undefined critique détecté !") return 0 if __name__ == "__main__": sys.exit(main())