# Document de Design: Visual Workflow Builder ## Vue d'Ensemble Le Visual Workflow Builder est une interface graphique révolutionnaire qui permet de créer des workflows RPA par glisser-déposer, sans écrire une seule ligne de code. Cette solution démocratise l'automatisation RPA en la rendant accessible aux utilisateurs non-techniques tout en conservant la puissance nécessaire aux développeurs experts. ### Objectifs de Design 1. **Simplicité d'Usage**: Interface intuitive inspirée de Scratch et Node-RED 2. **Puissance Technique**: Accès à toutes les fonctionnalités RPA Vision V3 3. **Performance**: Rendu fluide à 60fps même avec 100+ nodes 4. **Intégration**: Compatibilité totale avec ExecutionLoop, Analytics, Self-Healing 5. **Extensibilité**: Architecture modulaire pour ajouter facilement de nouveaux types de nodes ### Principes de Design - **WYSIWYG**: Ce que vous voyez est ce que vous obtenez - **Feedback Immédiat**: Test et validation en temps réel - **Tolérance aux Erreurs**: Validation continue et récupération gracieuse - **Accessibilité**: Support clavier, screen readers, high contrast - **Collaboration**: Partage et versioning des workflows --- ## Architecture Globale ### Vue d'Ensemble de l'Architecture ``` ┌─────────────────────────────────────────────────────────────────┐ │ Visual Workflow Builder │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │ │ Frontend │ │ Backend API │ │ Integration │ │ │ │ (React/Vue) │ │ (Flask) │ │ Layer │ │ │ │ │ │ │ │ │ │ │ │ • Canvas │ │ • Serialization │ │ • WorkflowGraph │ │ │ │ • Palette │ │ • Validation │ │ • ExecutionLoop │ │ │ │ • Properties │ │ • Templates │ │ • Analytics │ │ │ │ • Target Select │ │ • Export/Import │ │ • Self-Healing │ │ │ │ • Real-time │ │ • Version Ctrl │ │ • Storage │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ │ │ │ │ │ │ └─────────────────────┼─────────────────────┘ │ │ │ │ ├─────────────────────────────────┼─────────────────────────────────┤ │ Communication Layer (WebSocket + REST) │ ├─────────────────────────────────┼─────────────────────────────────┤ │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ RPA Vision V3 Core System │ │ │ │ │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │ExecutionLoop│ │ Analytics │ │Self-Healing │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ └─────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ ``` ### Couches Architecturales #### 1. Couche Présentation (Frontend) - **Framework**: React avec TypeScript - **Canvas**: react-flow-renderer pour le rendu des graphes - **Drag & Drop**: react-dnd pour les interactions - **UI Components**: Material-UI ou Ant Design - **State Management**: Redux Toolkit - **Real-time**: Socket.IO client #### 2. Couche API (Backend) - **Framework**: Flask avec Flask-SocketIO - **Sérialisation**: JSON Schema validation - **Base de Données**: SQLite pour les workflows, PostgreSQL pour production - **Cache**: Redis pour les sessions et templates - **Files**: Système de fichiers pour les exports #### 3. Couche Intégration - **Conversion**: Visual JSON → WorkflowGraph - **Exécution**: Interface avec ExecutionLoop - **Monitoring**: Hooks vers Analytics - **Recovery**: Intégration Self-Healing --- ## Composants et Interfaces ### 1. Canvas Component Le Canvas est le composant central où les utilisateurs construisent visuellement leurs workflows. #### Responsabilités - Rendu des nodes et edges - Gestion des interactions (sélection, déplacement) - Zoom et panoramique - Grille d'alignement - Minimap pour navigation #### Interface TypeScript ```typescript interface CanvasProps { nodes: VisualNode[]; edges: VisualEdge[]; onNodeSelect: (nodeId: string) => void; onNodeMove: (nodeId: string, position: Position) => void; onEdgeCreate: (source: string, target: string) => void; onEdgeDelete: (edgeId: string) => void; readonly?: boolean; } interface CanvasState { selectedNodes: string[]; selectedEdges: string[]; viewport: Viewport; dragState: DragState; } ``` ### 2. Node Palette La palette affiche tous les types de nodes disponibles organisés par catégorie. #### Responsabilités - Affichage des types de nodes disponibles - Recherche et filtrage - Catégorisation (Actions, Logic, Data, etc.) - Drag source pour création de nodes #### Structure ```typescript interface NodeType { id: string; name: string; category: NodeCategory; icon: string; description: string; parameters: ParameterDefinition[]; inputPorts: PortDefinition[]; outputPorts: PortDefinition[]; } enum NodeCategory { ACTION = 'action', // Click, Type, Wait LOGIC = 'logic', // If, Loop, Switch DATA = 'data', // Variable, Extract, Transform FLOW = 'flow', // Start, End, Subworkflow INTEGRATION = 'integration' // API, Database, File } ``` ### 3. Properties Panel Le panneau de propriétés permet de configurer les paramètres de chaque node. #### Responsabilités - Configuration des paramètres de nodes - Validation en temps réel - Target selector integration - Variable management - Help et documentation contextuelle #### Composants ```typescript interface PropertyEditor { parameter: ParameterDefinition; value: any; onChange: (value: any) => void; onValidate: (value: any) => ValidationResult; } // Types d'éditeurs class TextEditor extends PropertyEditor {} class NumberEditor extends PropertyEditor {} class BooleanEditor extends PropertyEditor {} class SelectEditor extends PropertyEditor {} class TargetEditor extends PropertyEditor {} // Sélection interactive class VariableEditor extends PropertyEditor {} class ExpressionEditor extends PropertyEditor {} // Conditions ``` ### 4. Target Selector Le sélecteur de cible permet de sélectionner interactivement des éléments UI à l'écran. #### Workflow ```python class TargetSelector: async def start_selection(self) -> TargetInfo: """Démarre le processus de sélection interactive""" # 1. Capturer l'écran screenshot = await self.capture_screen() # 2. Entrer en mode sélection self.enter_selection_mode(screenshot) # 3. Attendre la sélection utilisateur element = await self.wait_for_user_selection() # 4. Extraire les propriétés target_info = await self.extract_element_properties(element) return target_info async def extract_element_properties(self, element) -> TargetInfo: """Extrait toutes les propriétés d'un élément""" return TargetInfo( text=element.text_content, tag_name=element.tag_name, class_name=element.class_name, position=self.get_element_position(element), size=self.get_element_size(element), embedding=await self.generate_clip_embedding(element) ) ``` ### 5. Execution Engine Le moteur d'exécution convertit les workflows visuels en WorkflowGraph et les exécute. #### Conversion Pipeline ```python class VisualToGraphConverter: def convert(self, visual_workflow: VisualWorkflow) -> WorkflowGraph: """Convertit un workflow visuel en WorkflowGraph""" # 1. Valider la structure self.validate_structure(visual_workflow) # 2. Convertir les nodes nodes = [self.convert_node(node) for node in visual_workflow.nodes] # 3. Convertir les edges edges = [self.convert_edge(edge) for edge in visual_workflow.edges] # 4. Créer le graph return WorkflowGraph(nodes, edges) def convert_node(self, visual_node: VisualNode) -> WorkflowNode: """Convertit un node visuel en WorkflowNode""" if visual_node.type == 'click': return ClickNode( target=visual_node.parameters['target'], timeout=visual_node.parameters.get('timeout', 5000), retries=visual_node.parameters.get('retries', 3) ) elif visual_node.type == 'type': return TypeNode( target=visual_node.parameters['target'], text=visual_node.parameters['text'], clear_first=visual_node.parameters.get('clear_first', False) ) # ... autres types ``` --- ## Modèles de Données ### 1. Visual Workflow ```python @dataclass class VisualWorkflow: """Représentation complète d'un workflow visuel""" id: str name: str description: Optional[str] version: str created_at: datetime updated_at: datetime created_by: str # Structure visuelle nodes: List[VisualNode] edges: List[VisualEdge] # Configuration variables: List[Variable] settings: WorkflowSettings # Métadonnées tags: List[str] category: Optional[str] is_template: bool = False ``` ### 2. Visual Node ```python @dataclass class VisualNode: """Représentation d'un node dans le canvas""" id: str type: str # 'click', 'type', 'wait', 'if', 'loop', etc. # Position visuelle position: Position size: Size # Configuration parameters: Dict[str, Any] # Connexions input_ports: List[Port] output_ports: List[Port] # État visuel selected: bool = False highlighted: bool = False status: Optional[NodeStatus] = None # pendant l'exécution # Métadonnées label: Optional[str] = None description: Optional[str] = None color: Optional[str] = None class NodeStatus(Enum): """États possibles d'un node pendant l'exécution""" IDLE = 'idle' RUNNING = 'running' SUCCESS = 'success' FAILED = 'failed' SKIPPED = 'skipped' ``` ### 3. Visual Edge ```python @dataclass class VisualEdge: """Représentation d'une connexion entre nodes""" id: str source: str # node ID target: str # node ID source_port: str target_port: str # Condition (pour les branches) condition: Optional[EdgeCondition] = None # Style visuel style: Optional[EdgeStyle] = None # État selected: bool = False animated: bool = False # pendant l'exécution @dataclass class EdgeCondition: """Condition pour l'exécution d'un edge""" type: str # 'always', 'success', 'failure', 'expression' expression: Optional[str] = None # pour type 'expression' ``` ### 4. Parameter Definitions ```python @dataclass class ParameterDefinition: """Définition d'un paramètre de node""" name: str type: ParameterType required: bool default_value: Optional[Any] = None # Validation validation: Optional[List[ValidationRule]] = None # UI label: str = "" description: Optional[str] = None placeholder: Optional[str] = None # Comportement spécial is_target: bool = False # Active le target selector is_variable: bool = False # Permet ${variable} is_expression: bool = False # Permet les expressions class ParameterType(Enum): """Types de paramètres supportés""" STRING = 'string' NUMBER = 'number' BOOLEAN = 'boolean' SELECT = 'select' TARGET = 'target' VARIABLE = 'variable' EXPRESSION = 'expression' FILE = 'file' ``` ### 5. Templates ```python @dataclass class WorkflowTemplate: """Template de workflow pré-construit""" id: str name: str description: str category: str # Template data workflow: VisualWorkflow # Paramètres configurables parameters: List[TemplateParameter] # Métadonnées tags: List[str] difficulty: str # 'beginner', 'intermediate', 'advanced' estimated_time: int # minutes # Usage usage_count: int = 0 rating: float = 0.0 @dataclass class TemplateParameter: """Paramètre configurable d'un template""" name: str type: ParameterType description: str default_value: Optional[Any] = None # Mapping vers les nodes node_id: str parameter_name: str ``` --- ## Correctness Properties *Une propriété est une caractéristique ou un comportement qui doit être vrai pour toutes les exécutions valides d'un système - essentiellement, une déclaration formelle sur ce que le système doit faire. Les propriétés servent de pont entre les spécifications lisibles par l'humain et les garanties de correction vérifiables par machine.* ### Propriétés Structurelles #### Propriété 1: Suppression de Node Cascade *Pour tout* workflow et tout node, quand un node est supprimé, tous les edges connectés à ce node doivent également être supprimés automatiquement. **Valide: Exigences 1.5, 2.4** #### Propriété 2: Validation de Connexion *Pour tout* edge créé entre deux nodes, la connexion doit être validée selon les règles de compatibilité des ports (type de sortie compatible avec type d'entrée). **Valide: Exigences 2.2** #### Propriété 3: Détection de Cycles *Pour tout* workflow, si on ajoute un edge qui créerait un cycle (sauf pour les boucles explicites), le système doit détecter et rejeter cette connexion. **Valide: Exigences 12.4** #### Propriété 4: Nodes Déconnectés *Pour tout* workflow, tous les nodes (sauf Start et End) doivent avoir au moins un edge entrant et un edge sortant, sinon un avertissement doit être affiché. **Valide: Exigences 12.3** ### Propriétés de Sérialisation #### Propriété 5: Round-trip de Sérialisation *Pour tout* workflow visuel, sérialiser puis désérialiser doit produire un workflow équivalent (même nodes, edges, paramètres, variables). **Valide: Exigences 5.1, 5.2** #### Propriété 6: Génération d'ID Unique *Pour tout* nouveau workflow sans ID, la sauvegarde doit générer un ID unique qui n'existe pas déjà dans le système. **Valide: Exigences 5.4** #### Propriété 7: Validation de Champs Requis *Pour tout* workflow lors de la sérialisation, tous les champs requis (id, name, nodes, edges) doivent être présents, sinon la sérialisation doit échouer. **Valide: Exigences 5.3** ### Propriétés de Configuration #### Propriété 8: Validation de Paramètres Requis *Pour tout* node, tous les paramètres marqués comme requis doivent avoir une valeur non-nulle, sinon le node doit afficher un indicateur d'avertissement. **Valide: Exigences 12.2** #### Propriété 9: Valeurs par Défaut *Pour tout* nouveau node créé, tous les paramètres avec une valeur par défaut définie doivent être pré-remplis avec cette valeur. **Valide: Exigences 3.5** #### Propriété 10: Validation de Variables *Pour tout* paramètre contenant une référence de variable ${nom}, la variable doit exister dans la liste des variables du workflow, sinon une erreur de validation doit être affichée. **Valide: Exigences 10.3** #### Propriété 11: Unicité des Noms de Variables *Pour tout* workflow, tous les noms de variables doivent être uniques (pas de doublons). **Valide: Exigences 10.2** ### Propriétés d'Exécution #### Propriété 12: Conversion Valide *Pour tout* workflow visuel valide, la conversion en WorkflowGraph doit produire un graphe exécutable par ExecutionLoop sans erreur. **Valide: Exigences 6.1, 18.1** #### Propriété 13: Synchronisation d'État *Pour tout* node pendant l'exécution, l'état visuel (status) doit refléter l'état d'exécution réel (idle → running → success/failed). **Valide: Exigences 6.3** #### Propriété 14: Substitution de Variables *Pour tout* paramètre contenant ${variable} lors de l'exécution, la référence doit être remplacée par la valeur runtime de la variable. **Valide: Exigences 10.4** #### Propriété 15: Exécution de Conditions *Pour tout* node Condition, l'exécution doit suivre le edge "true" si la condition est vraie, et le edge "false" si la condition est fausse. **Valide: Exigences 8.3** #### Propriété 16: Exécution de Boucles *Pour tout* node Loop, le corps de la boucle doit être exécuté le nombre de fois spécifié par les paramètres (count pour repeat, condition pour while, items pour for-each). **Valide: Exigences 9.3** ### Propriétés d'Interaction #### Propriété 17: Annuler/Refaire Cohérence *Pour toute* séquence d'actions, effectuer une action puis l'annuler doit restaurer l'état précédent, et refaire doit restaurer l'état après l'action. **Valide: Exigences 13.2, 13.3** #### Propriété 18: Pile d'Annulation *Pour toute* action effectuée, elle doit être ajoutée à la pile d'annulation, et on doit pouvoir annuler au moins 50 actions. **Valide: Exigences 13.1, 13.4** #### Propriété 19: Invalidation de Refaire *Pour toute* nouvelle action effectuée après une annulation, la pile de refaire doit être vidée. **Valide: Exigences 13.5** #### Propriété 20: Extraction de Propriétés de Cible *Pour tout* élément sélectionné via le target selector, les propriétés extraites doivent inclure au minimum: text, position, size, et embedding. **Valide: Exigences 4.5** ### Propriétés de Templates #### Propriété 21: Chargement de Template *Pour tout* template sélectionné, le chargement doit créer un workflow avec tous les nodes et edges du template, et tous les paramètres doivent être modifiables. **Valide: Exigences 11.2, 11.3** #### Propriété 22: Sauvegarde comme Template *Pour tout* workflow créé par un utilisateur, il doit être possible de le sauvegarder comme template personnalisé avec des paramètres configurables. **Valide: Exigences 11.5** ### Propriétés d'Export/Import #### Propriété 23: Complétude d'Export *Pour tout* workflow exporté, le fichier JSON/YAML doit contenir tous les nodes, edges, variables, et métadonnées nécessaires pour reconstruire le workflow. **Valide: Exigences 15.1, 15.3** #### Propriété 24: Validation d'Import *Pour tout* fichier importé, le système doit valider le format et rejeter les fichiers invalides avec un message d'erreur clair. **Valide: Exigences 15.2** #### Propriété 25: Migration de Version *Pour tout* workflow d'une version antérieure, le système doit détecter l'incompatibilité et offrir une migration automatique vers la version actuelle. **Valide: Exigences 15.4** ### Propriétés d'Intégration #### Propriété 26: Compatibilité de Format *Pour tout* workflow sauvegardé par le Visual Builder, il doit être dans le même format que les workflows créés programmatiquement et vice-versa. **Valide: Exigences 18.4** #### Propriété 27: Intégration Self-Healing *Pour tout* workflow utilisant self-healing, les nodes doivent être configurés avec les stratégies de récupération appropriées lors de la conversion. **Valide: Exigences 18.2** #### Propriété 28: Intégration Analytics *Pour tout* workflow exécuté, les métriques doivent être automatiquement collectées et envoyées au système Analytics. **Valide: Exigences 18.3** #### Propriété 29: Rétrocompatibilité *Pour tout* workflow existant créé programmatiquement (suivant le format standard), le Visual Builder doit pouvoir le charger, le visualiser et l'éditer. **Valide: Exigences 18.5** ### Propriétés de Recherche et Filtrage #### Propriété 30: Filtrage de Palette *Pour toute* requête de recherche dans la palette, seuls les types de nodes dont le nom ou la description contient la requête doivent être affichés. **Valide: Exigences 7.2** #### Propriété 31: Extensibilité de Palette *Pour tout* nouveau type de node ajouté au système, il doit automatiquement apparaître dans la palette dans la catégorie appropriée. **Valide: Exigences 7.5** ### Propriétés de Zoom et Navigation #### Propriété 32: Zoom Centré *Pour tout* événement de zoom (molette de souris), le zoom doit être centré sur la position du curseur, pas sur le centre du canvas. **Valide: Exigences 14.1** #### Propriété 33: Fit-to-Screen *Pour tout* workflow, la fonction "ajuster à l'écran" doit calculer le zoom et la position pour que tous les nodes soient visibles dans le viewport. **Valide: Exigences 14.3** --- ## Gestion des Erreurs ### Stratégies de Gestion d'Erreurs #### 1. Erreurs de Validation - **Détection**: Validation en temps réel pendant l'édition - **Affichage**: Indicateurs visuels sur les nodes/edges problématiques - **Blocage**: Empêcher l'exécution si des erreurs critiques existent - **Récupération**: Suggestions de correction automatique quand possible #### 2. Erreurs d'Exécution - **Capture**: Intercepter toutes les exceptions pendant l'exécution - **Affichage**: Mettre en surbrillance le node en échec avec message d'erreur - **Logging**: Enregistrer tous les détails pour debugging - **Récupération**: Intégration avec Self-Healing pour tentatives automatiques #### 3. Erreurs de Sérialisation - **Validation**: Vérifier le schéma JSON avant sauvegarde/chargement - **Affichage**: Messages d'erreur clairs avec détails du problème - **Récupération**: Tentative de migration automatique pour anciennes versions - **Backup**: Sauvegarder l'état précédent avant toute modification #### 4. Erreurs Réseau - **Retry**: Réessayer automatiquement les requêtes échouées (3 tentatives) - **Timeout**: Timeouts configurables pour toutes les opérations réseau - **Offline**: Mode dégradé avec sauvegarde locale si serveur inaccessible - **Feedback**: Indicateurs de statut de connexion en temps réel ### Codes d'Erreur ```python class ErrorCode(Enum): # Validation MISSING_REQUIRED_PARAMETER = 1001 INVALID_PARAMETER_TYPE = 1002 INVALID_VARIABLE_REFERENCE = 1003 CIRCULAR_DEPENDENCY = 1004 DISCONNECTED_NODE = 1005 # Sérialisation INVALID_JSON_FORMAT = 2001 MISSING_REQUIRED_FIELD = 2002 VERSION_INCOMPATIBLE = 2003 # Exécution CONVERSION_FAILED = 3001 EXECUTION_FAILED = 3002 TARGET_NOT_FOUND = 3003 # Réseau CONNECTION_FAILED = 4001 TIMEOUT = 4002 SERVER_ERROR = 4003 ``` --- ## Stratégie de Test ### 1. Tests Unitaires Les tests unitaires vérifient le comportement de composants individuels. #### Frontend Components ```typescript describe('Canvas Component', () => { test('should create node on drop', () => { const canvas = render(); const nodeType = { type: 'click', name: 'Click' }; fireEvent.drop(canvas, { dataTransfer: { getData: () => nodeType } }); expect(canvas.nodes).toHaveLength(1); expect(canvas.nodes[0].type).toBe('click'); }); test('should select node on click', () => { const onSelect = jest.fn(); const nodes = [createMockNode('click')]; const canvas = render(); fireEvent.click(canvas.getByTestId('node-click-1')); expect(onSelect).toHaveBeenCalledWith('click-1'); }); }); describe('Properties Panel', () => { test('should display required parameter warning', () => { const node = createMockNode('click', { target: '' }); const panel = render(); expect(panel.getByText('Target is required')).toBeInTheDocument(); }); test('should validate parameter on change', () => { const node = createMockNode('wait', { duration: -1 }); const panel = render(); expect(panel.getByText('Duration must be positive')).toBeInTheDocument(); }); }); ``` #### Backend API ```python class TestWorkflowAPI(unittest.TestCase): def test_create_workflow(self): """Test création d'un nouveau workflow""" response = self.client.post('/api/workflows', json={ 'name': 'Test Workflow', 'description': 'Test description' }) self.assertEqual(response.status_code, 201) data = response.get_json() self.assertIn('id', data) self.assertEqual(data['name'], 'Test Workflow') def test_serialize_workflow(self): """Test sérialisation complète""" workflow = create_test_workflow_with_nodes(5) serialized = serialize_workflow(workflow) self.assertIn('nodes', serialized) self.assertIn('edges', serialized) self.assertEqual(len(serialized['nodes']), 5) def test_validate_required_fields(self): """Test validation des champs requis""" invalid_workflow = {'name': 'Test'} # manque 'nodes' et 'edges' with self.assertRaises(ValidationError): validate_workflow(invalid_workflow) ``` ### 2. Tests d'Intégration Les tests d'intégration vérifient l'interaction entre composants. ```python class TestVisualToGraphConversion(unittest.TestCase): def test_simple_workflow_conversion(self): """Test conversion d'un workflow simple""" visual_workflow = { 'nodes': [ {'id': '1', 'type': 'start', 'position': {'x': 0, 'y': 0}}, {'id': '2', 'type': 'click', 'position': {'x': 100, 'y': 0}, 'parameters': {'target': 'Button "Login"', 'timeout': 5000}}, {'id': '3', 'type': 'end', 'position': {'x': 200, 'y': 0}} ], 'edges': [ {'id': 'e1', 'source': '1', 'target': '2'}, {'id': 'e2', 'source': '2', 'target': '3'} ] } converter = VisualToGraphConverter() workflow_graph = converter.convert(visual_workflow) self.assertEqual(len(workflow_graph.nodes), 3) self.assertEqual(workflow_graph.nodes[1].type, 'click') self.assertEqual(workflow_graph.nodes[1].parameters['target'], 'Button "Login"') def test_workflow_with_conditions(self): """Test conversion avec conditions""" visual_workflow = create_workflow_with_condition() converter = VisualToGraphConverter() workflow_graph = converter.convert(visual_workflow) # Vérifier que les branches true/false sont correctes condition_node = workflow_graph.get_node_by_type('condition') self.assertEqual(len(condition_node.output_edges), 2) true_edge = [e for e in condition_node.output_edges if e.condition.type == 'true'][0] false_edge = [e for e in condition_node.output_edges if e.condition.type == 'false'][0] self.assertIsNotNone(true_edge) self.assertIsNotNone(false_edge) ``` ### 3. Tests Property-Based Les tests property-based vérifient que les propriétés universelles sont respectées pour tous les inputs. **Framework**: Hypothesis (Python) ou fast-check (TypeScript) ```python from hypothesis import given, strategies as st from hypothesis.stateful import RuleBasedStateMachine, rule, invariant class TestWorkflowProperties(unittest.TestCase): @given(st.lists(st.text(min_size=1), min_size=1, max_size=20)) def test_property_5_serialization_roundtrip(self, node_names): """ Feature: visual-workflow-builder, Property 5: Round-trip de Sérialisation Pour tout workflow, sérialiser puis désérialiser doit produire un workflow équivalent """ # Créer un workflow avec des nodes aléatoires workflow = create_workflow_with_node_names(node_names) # Sérialiser serialized = serialize_workflow(workflow) # Désérialiser deserialized = deserialize_workflow(serialized) # Vérifier l'équivalence self.assertEqual(len(workflow.nodes), len(deserialized.nodes)) self.assertEqual(len(workflow.edges), len(deserialized.edges)) for i, node in enumerate(workflow.nodes): self.assertEqual(node.type, deserialized.nodes[i].type) self.assertEqual(node.parameters, deserialized.nodes[i].parameters) @given(st.integers(min_value=1, max_value=10)) def test_property_1_node_deletion_cascade(self, num_nodes): """ Feature: visual-workflow-builder, Property 1: Suppression de Node Cascade Pour tout workflow, supprimer un node doit supprimer tous ses edges """ # Créer un workflow linéaire avec num_nodes nodes workflow = create_linear_workflow(num_nodes) initial_edge_count = len(workflow.edges) # Supprimer un node au milieu middle_node = workflow.nodes[num_nodes // 2] edges_connected_to_node = [e for e in workflow.edges if e.source == middle_node.id or e.target == middle_node.id] workflow.delete_node(middle_node.id) # Vérifier que les edges connectés sont supprimés self.assertEqual(len(workflow.edges), initial_edge_count - len(edges_connected_to_node)) for edge in edges_connected_to_node: self.assertNotIn(edge, workflow.edges) @given(st.lists(st.text(min_size=1, max_size=20), min_size=1, max_size=10)) def test_property_11_variable_name_uniqueness(self, variable_names): """ Feature: visual-workflow-builder, Property 11: Unicité des Noms de Variables Pour tout workflow, tous les noms de variables doivent être uniques """ workflow = VisualWorkflow(id='test', name='Test', nodes=[], edges=[], variables=[]) # Essayer d'ajouter des variables for name in variable_names: try: workflow.add_variable(name, 'string', '') except ValueError: # Nom dupliqué, c'est attendu pass # Vérifier l'unicité variable_names_in_workflow = [v.name for v in workflow.variables] self.assertEqual(len(variable_names_in_workflow), len(set(variable_names_in_workflow))) ``` @given(st.integers(min_value=2, max_value=50)) def test_property_18_undo_redo_stack_capacity(self, num_actions): """ Feature: visual-workflow-builder, Property 18: Pile d'Annulation Pour toute action, on doit pouvoir annuler au moins 50 actions """ workflow = VisualWorkflow(id='test', name='Test', nodes=[], edges=[], variables=[]) undo_manager = UndoManager(workflow) # Effectuer num_actions actions for i in range(num_actions): node = VisualNode(id=f'node-{i}', type='click', position={'x': i*100, 'y': 0}, parameters={}, input_ports=[], output_ports=[]) undo_manager.execute(AddNodeAction(node)) # Vérifier qu'on peut annuler au moins min(50, num_actions) actions max_undo = min(50, num_actions) for i in range(max_undo): self.assertTrue(undo_manager.can_undo()) undo_manager.undo() self.assertEqual(len(workflow.nodes), num_actions - max_undo) @given(st.text(min_size=1, max_size=50)) def test_property_10_variable_reference_validation(self, variable_name): """ Feature: visual-workflow-builder, Property 10: Validation de Variables Pour tout paramètre avec ${variable}, la variable doit exister """ workflow = VisualWorkflow(id='test', name='Test', nodes=[], edges=[], variables=[]) # Créer un node avec référence de variable node = VisualNode( id='node-1', type='type', position={'x': 0, 'y': 0}, parameters={'text': f'${{{variable_name}}}'}, input_ports=[], output_ports=[] ) workflow.nodes.append(node) # Valider validation_result = validate_workflow(workflow) # Si la variable n'existe pas, doit avoir une erreur if variable_name not in [v.name for v in workflow.variables]: self.assertFalse(validation_result.is_valid) self.assertTrue(any('variable' in err.lower() for err in validation_result.errors)) else: self.assertTrue(validation_result.is_valid) class WorkflowStateMachine(RuleBasedStateMachine): """ Machine à états pour tester les propriétés de manière stateful """ def __init__(self): super().__init__() self.workflow = VisualWorkflow(id='test', name='Test', nodes=[], edges=[], variables=[]) self.undo_manager = UndoManager(self.workflow) @rule() def add_node(self): """Ajouter un node aléatoire""" node_id = f'node-{len(self.workflow.nodes)}' node = VisualNode(id=node_id, type='click', position={'x': 0, 'y': 0}, parameters={}, input_ports=[], output_ports=[]) self.undo_manager.execute(AddNodeAction(node)) @rule() def delete_node(self): """Supprimer un node aléatoire""" if self.workflow.nodes: node = random.choice(self.workflow.nodes) self.undo_manager.execute(DeleteNodeAction(node.id)) @rule() def undo(self): """Annuler la dernière action""" if self.undo_manager.can_undo(): self.undo_manager.undo() @rule() def redo(self): """Refaire la dernière action annulée""" if self.undo_manager.can_redo(): self.undo_manager.redo() @invariant() def nodes_have_unique_ids(self): """Invariant: tous les nodes ont des IDs uniques""" node_ids = [n.id for n in self.workflow.nodes] assert len(node_ids) == len(set(node_ids)) @invariant() def edges_reference_existing_nodes(self): """Invariant: tous les edges référencent des nodes existants""" node_ids = {n.id for n in self.workflow.nodes} for edge in self.workflow.edges: assert edge.source in node_ids assert edge.target in node_ids TestWorkflowStateMachine = WorkflowStateMachine.TestCase ``` ### 4. Tests End-to-End Les tests end-to-end vérifient le système complet du frontend au backend. ```python class TestEndToEndWorkflow(unittest.TestCase): def test_complete_workflow_creation_and_execution(self): """Test du cycle complet: création → sauvegarde → exécution""" # 1. Créer un workflow via l'API response = self.client.post('/api/workflows', json={ 'name': 'Login Workflow', 'description': 'Test login automation' }) workflow_id = response.get_json()['id'] # 2. Ajouter des nodes nodes = [ {'id': '1', 'type': 'start', 'position': {'x': 0, 'y': 0}}, {'id': '2', 'type': 'navigate', 'position': {'x': 100, 'y': 0}, 'parameters': {'url': 'https://example.com/login'}}, {'id': '3', 'type': 'type', 'position': {'x': 200, 'y': 0}, 'parameters': {'target': 'input[name="username"]', 'text': 'testuser'}}, {'id': '4', 'type': 'type', 'position': {'x': 300, 'y': 0}, 'parameters': {'target': 'input[name="password"]', 'text': 'testpass'}}, {'id': '5', 'type': 'click', 'position': {'x': 400, 'y': 0}, 'parameters': {'target': 'button[type="submit"]'}}, {'id': '6', 'type': 'end', 'position': {'x': 500, 'y': 0}} ] edges = [ {'id': 'e1', 'source': '1', 'target': '2'}, {'id': 'e2', 'source': '2', 'target': '3'}, {'id': 'e3', 'source': '3', 'target': '4'}, {'id': 'e4', 'source': '4', 'target': '5'}, {'id': 'e5', 'source': '5', 'target': '6'} ] # 3. Mettre à jour le workflow response = self.client.put(f'/api/workflows/{workflow_id}', json={ 'nodes': nodes, 'edges': edges }) self.assertEqual(response.status_code, 200) # 4. Valider le workflow response = self.client.post(f'/api/workflows/{workflow_id}/validate') validation = response.get_json() self.assertTrue(validation['is_valid']) # 5. Exécuter le workflow response = self.client.post(f'/api/workflows/{workflow_id}/execute', json={ 'mode': 'test' }) execution_id = response.get_json()['execution_id'] # 6. Attendre la completion self.wait_for_execution_completion(execution_id, timeout=30) # 7. Vérifier les résultats response = self.client.get(f'/api/executions/{execution_id}') execution = response.get_json() self.assertEqual(execution['status'], 'completed') self.assertTrue(execution['success']) self.assertEqual(len(execution['node_results']), 6) ``` ### 5. Tests de Performance ```python class TestPerformance(unittest.TestCase): def test_large_workflow_serialization(self): """Test performance de sérialisation avec 100 nodes""" workflow = create_workflow_with_nodes(100) start_time = time.time() serialized = serialize_workflow(workflow) end_time = time.time() # Doit être < 1 seconde self.assertLess(end_time - start_time, 1.0) self.assertEqual(len(serialized['nodes']), 100) def test_workflow_validation_performance(self): """Test performance de validation avec workflow complexe""" workflow = create_complex_workflow_with_conditions_and_loops(50) start_time = time.time() validation_result = validate_workflow(workflow) end_time = time.time() # Doit être < 100ms self.assertLess(end_time - start_time, 0.1) self.assertTrue(validation_result.is_valid) ``` --- ## API Design ### REST Endpoints #### Workflows ``` GET /api/workflows # Liste tous les workflows POST /api/workflows # Crée un nouveau workflow GET /api/workflows/:id # Récupère un workflow spécifique PUT /api/workflows/:id # Met à jour un workflow DELETE /api/workflows/:id # Supprime un workflow POST /api/workflows/:id/validate # Valide un workflow POST /api/workflows/:id/execute # Exécute un workflow GET /api/workflows/:id/export # Exporte un workflow (JSON/YAML) POST /api/workflows/import # Importe un workflow ``` #### Templates ``` GET /api/templates # Liste tous les templates POST /api/templates # Crée un nouveau template GET /api/templates/:id # Récupère un template spécifique POST /api/templates/:id/instantiate # Crée un workflow depuis un template ``` #### Node Types ``` GET /api/node-types # Liste tous les types de nodes disponibles GET /api/node-types/:type # Récupère la définition d'un type spécifique ``` #### Executions ``` GET /api/executions/:id # Récupère le statut d'une exécution POST /api/executions/:id/cancel # Annule une exécution en cours ``` ### WebSocket Events #### Client → Server ```typescript // S'abonner aux mises à jour d'exécution { type: 'subscribe_execution', execution_id: string } // Se désabonner { type: 'unsubscribe_execution', execution_id: string } ``` #### Server → Client ```typescript // Statut d'exécution { type: 'execution_status', execution_id: string, status: 'running' | 'completed' | 'failed', progress: number, // 0-100 current_node_id?: string } // Statut d'un node { type: 'node_status', execution_id: string, node_id: string, status: 'running' | 'success' | 'failed' | 'skipped', duration?: number, error?: string } // Résultat final { type: 'execution_complete', execution_id: string, success: boolean, duration: number, node_results: NodeResult[] } ``` --- ## Considérations de Sécurité ### 1. Validation d'Entrée - **Sanitization**: Tous les inputs utilisateur doivent être nettoyés pour prévenir les injections - **Schema Validation**: Utiliser JSON Schema pour valider tous les workflows - **Size Limits**: Limiter la taille des workflows (max 1000 nodes, max 10MB) - **Rate Limiting**: Limiter les requêtes API (100 req/min par utilisateur) ### 2. Exécution Sécurisée - **Sandboxing**: Exécuter les workflows dans un environnement isolé - **Permissions**: Contrôle d'accès basé sur les rôles (RBAC) - **Audit Trail**: Logger toutes les actions utilisateur avec timestamps - **Resource Limits**: Limiter CPU (80%), mémoire (2GB), durée (30min) ### 3. Stockage Sécurisé - **Encryption**: Chiffrer les workflows sensibles au repos (AES-256) - **Access Control**: Permissions granulaires (owner, editor, viewer) - **Backup**: Sauvegardes automatiques quotidiennes avec rétention 30 jours - **Versioning**: Historique complet des modifications avec rollback ### 4. Communication Sécurisée - **HTTPS**: Toutes les communications via TLS 1.3 - **Authentication**: JWT tokens avec expiration (1h) et refresh (7j) - **CORS**: Configuration stricte des origines autorisées - **WebSocket**: Authentification et chiffrement des messages --- ## Plan de Déploiement ### Phase 1: MVP (4-6 semaines) #### Backend - API REST de base (CRUD workflows) - Conversion Visual → WorkflowGraph - Exécution simple via ExecutionLoop - Templates de base (Login, Form Fill) - Validation structurelle #### Frontend - Canvas avec drag & drop basique - Palette avec nodes essentiels (Click, Type, Wait, Navigate, If, Loop) - Properties panel simple - Target selector basique - Sauvegarde/Chargement #### Tests - Tests unitaires critiques (>70% coverage) - Tests d'intégration de base - Validation manuelle des workflows ### Phase 2: Fonctionnalités Avancées (4-6 semaines) #### Backend - WebSocket pour temps réel - Templates avancés (Data Extraction, API Integration) - Export/Import (JSON, YAML) - Validation avancée (cycles, variables) - Migration de versions #### Frontend - Undo/Redo complet - Zoom/Pan optimisé avec virtualisation - Variables management UI - Validation temps réel avec indicateurs - Raccourcis clavier - Minimap et fit-to-screen #### Tests - Tests de performance (100 nodes @ 60fps) - Property-based tests (Hypothesis) - Tests end-to-end automatisés - Tests de charge (10 utilisateurs simultanés) ### Phase 3: Production (2-4 semaines) #### Optimisations - Performance tuning (profiling, optimisations) - Caching intelligent (Redis pour templates) - Monitoring et métriques (Prometheus, Grafana) - Documentation utilisateur complète #### Sécurité - Audit de sécurité complet - Penetration testing - Hardening (rate limiting, input validation) - Backup et disaster recovery #### Déploiement - CI/CD pipeline (GitHub Actions) - Monitoring production (alertes, logs) - Rollback strategy (blue-green deployment) - Formation utilisateurs --- ## Métriques de Succès ### 1. Métriques Techniques #### Performance - **Rendu Canvas**: 60fps avec 100+ nodes ✓ - **Latence API**: < 200ms pour opérations CRUD ✓ - **Temps de Conversion**: Visual → Graph < 100ms ✓ - **Temps de Chargement**: < 2s pour workflows de 50 nodes ✓ - **Uptime**: > 99.9% ✓ #### Qualité - **Test Coverage**: > 80% pour code critique ✓ - **Bug Rate**: < 5 bugs critiques par release ✓ - **Property Tests**: 100% des propriétés testées ✓ - **Validation Rate**: > 95% des workflows valides avant exécution ✓ ### 2. Métriques Utilisateur #### Adoption - **Time to First Workflow**: < 5 minutes pour un utilisateur novice ✓ - **Workflow Creation Speed**: 50% plus rapide qu'écrire du code ✓ - **Error Rate**: < 5% de workflows invalides créés ✓ - **Completion Rate**: > 90% des workflows créés sont exécutés ✓ #### Satisfaction - **User Satisfaction**: > 4.5/5 dans les surveys ✓ - **NPS Score**: > 50 ✓ - **Feature Requests**: Priorisation basée sur votes utilisateurs ✓ - **Support Tickets**: < 10% liés au Visual Builder ✓ ### 3. Métriques Business #### Productivité - **Workflows Created**: 3x plus de workflows créés vs méthode manuelle ✓ - **Time Saved**: 60% de réduction du temps de développement ✓ - **Reusability**: 40% des workflows utilisent des templates ✓ - **Collaboration**: 30% des workflows sont partagés entre utilisateurs ✓ #### Adoption - **Active Users**: 80% des utilisateurs créent au moins 1 workflow ✓ - **Retention**: 70% des utilisateurs reviennent après 1 semaine ✓ - **Power Users**: 20% des utilisateurs créent 5+ workflows ✓ - **Template Usage**: 50% des nouveaux workflows partent d'un template ✓ --- ## Conclusion Le Visual Workflow Builder représente une évolution majeure de RPA Vision V3, transformant un outil technique en une solution accessible à tous. L'architecture proposée garantit: - **Simplicité d'usage** sans compromis sur la puissance - **Performance** adaptée aux workflows complexes (100+ nodes @ 60fps) - **Intégration parfaite** avec l'écosystème existant (ExecutionLoop, Analytics, Self-Healing) - **Extensibilité** pour les futures fonctionnalités (collaboration, AI-assisted design) - **Qualité** grâce à une stratégie de test complète (33 propriétés testées) Cette solution positionnera RPA Vision V3 comme le leader du marché RPA no-code/low-code, démocratisant l'automatisation pour tous les utilisateurs, des débutants aux experts. ### Prochaines Étapes 1. **Validation du Design**: Review avec les stakeholders 2. **Création du Plan d'Implémentation**: Tasks détaillées avec estimations 3. **Setup de l'Environnement**: Configuration du projet frontend/backend 4. **Développement Phase 1**: MVP en 4-6 semaines 5. **Tests et Itération**: Feedback utilisateurs et améliorations