Skip to content

Commit c63eb7a

Browse files
committed
Fix workflow editor node compatibility
1 parent 24c211d commit c63eb7a

4 files changed

Lines changed: 83 additions & 1 deletion

File tree

src/backend/bisheng/api/services/external_workflow.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ class ExternalWorkflowService:
3737
_DEFAULT_TARGET_HANDLE = 'left_handle'
3838
_DEFAULT_HORIZONTAL_NODE_GAP = 320
3939
_NOTE_NODE_TYPE = NodeType.NOTE.value
40+
_EDITOR_FLOW_NODE_TYPE = 'flowNode'
41+
_EDITOR_NOTE_NODE_TYPE = 'noteNode'
4042
_START_NODE_TYPE = NodeType.START.value
4143
_END_NODE_TYPE = NodeType.END.value
4244
_SENSITIVE_KEY_PATTERNS = (
@@ -682,6 +684,20 @@ def _build_scaffold_node(cls,
682684
raise NotFoundError(msg=f'Workflow node template not found: {node_type}')
683685
return node_payload
684686

687+
@classmethod
688+
def _normalize_editor_node_types(cls, graph_data: dict) -> dict:
689+
for node in graph_data.get('nodes', []):
690+
if not isinstance(node, dict):
691+
continue
692+
node_data = node.get('data')
693+
if not isinstance(node_data, dict):
694+
continue
695+
node_type = node_data.get('type')
696+
if not node_type:
697+
continue
698+
node['type'] = cls._EDITOR_NOTE_NODE_TYPE if node_type == cls._NOTE_NODE_TYPE else cls._EDITOR_FLOW_NODE_TYPE
699+
return graph_data
700+
685701
@classmethod
686702
def _ensure_create_graph_scaffold(cls, graph_data: dict) -> dict:
687703
if not isinstance(graph_data, dict):
@@ -692,6 +708,7 @@ def _ensure_create_graph_scaffold(cls, graph_data: dict) -> dict:
692708
return graph_data
693709

694710
updated_graph = copy.deepcopy(graph_data)
711+
cls._normalize_editor_node_types(updated_graph)
695712
start_nodes = cls._find_nodes_by_type(updated_graph, cls._START_NODE_TYPE)
696713
end_nodes = cls._find_nodes_by_type(updated_graph, cls._END_NODE_TYPE)
697714

@@ -1042,6 +1059,7 @@ async def update_workflow_draft(cls,
10421059
if name is not None and name != flow.name:
10431060
cls._assert_workflow_name_available(login_user, name, exclude_flow_id=flow.id)
10441061

1062+
graph_data = cls._normalize_editor_node_types(copy.deepcopy(graph_data))
10451063
if has_flow_updates:
10461064
if name is not None:
10471065
flow.name = name

src/backend/bisheng/api/services/flow.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,31 @@
3737

3838
class FlowService(BaseService):
3939

40+
@staticmethod
41+
def _normalize_workflow_editor_graph(graph_data: Optional[dict]) -> Optional[dict]:
42+
if not isinstance(graph_data, dict):
43+
return graph_data
44+
45+
nodes = graph_data.get('nodes')
46+
if not isinstance(nodes, list):
47+
return graph_data
48+
49+
normalized_graph = copy.deepcopy(graph_data)
50+
for node in normalized_graph.get('nodes', []):
51+
if not isinstance(node, dict):
52+
continue
53+
node_data = node.get('data')
54+
if not isinstance(node_data, dict):
55+
continue
56+
node_type = node_data.get('type')
57+
if not node_type:
58+
continue
59+
if node.get('type') in {'flowNode', 'noteNode'}:
60+
continue
61+
node['type'] = 'noteNode' if node_type == 'note' else 'flowNode'
62+
63+
return normalized_graph
64+
4065
@classmethod
4166
def get_version_list_by_flow(cls, user: UserPayload, flow_id: str) -> UnifiedResponseModel[List[FlowVersionRead]]:
4267
"""
@@ -233,6 +258,8 @@ async def get_one_flow(cls, login_user: UserPayload, flow_id: str, share_link: U
233258
raise UnAuthorizedError()
234259

235260
flow_info.logo = await cls.get_logo_share_link_async(flow_info.logo)
261+
if flow_info.flow_type == FlowType.WORKFLOW.value:
262+
flow_info.data = cls._normalize_workflow_editor_graph(flow_info.data)
236263

237264
return resp_200(data=flow_info)
238265

src/backend/bisheng/workflow/authoring/registry.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1012,7 +1012,7 @@ def create_graph_node_payload(node_type: str,
10121012

10131013
return {
10141014
'id': node_id,
1015-
'type': node_type,
1015+
'type': 'noteNode' if node_type == 'note' else 'flowNode',
10161016
'position': {
10171017
'x': position_x,
10181018
'y': position_y,

src/backend/test/test_external_workflow_service.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ def test_ensure_create_graph_scaffold_builds_minimal_start_end_graph(self):
198198
graph = ExternalWorkflowService._ensure_create_graph_scaffold({'nodes': [], 'edges': []})
199199

200200
self.assertEqual([node['data']['type'] for node in graph['nodes']], ['start', 'end'])
201+
self.assertEqual([node['type'] for node in graph['nodes']], ['flowNode', 'flowNode'])
201202
self.assertEqual(len(graph['edges']), 1)
202203
self.assertEqual(graph['edges'][0]['source'], graph['nodes'][0]['id'])
203204
self.assertEqual(graph['edges'][0]['target'], graph['nodes'][1]['id'])
@@ -224,6 +225,7 @@ def test_ensure_create_graph_scaffold_wraps_initial_node_with_start_and_end(self
224225
end_id = next(node_id for node_id, node_type in node_types.items() if node_type == 'end')
225226

226227
self.assertEqual(len(graph['nodes']), 3)
228+
self.assertEqual([node['type'] for node in graph['nodes']], ['flowNode', 'flowNode', 'flowNode'])
227229
self.assertEqual(len(graph['edges']), 2)
228230
self.assertTrue(any(edge['source'] == start_id and edge['target'] == 'input-1' for edge in graph['edges']))
229231
self.assertTrue(any(edge['source'] == 'input-1' and edge['target'] == end_id for edge in graph['edges']))
@@ -258,6 +260,7 @@ def test_ensure_create_graph_scaffold_adds_condition_routes_to_end(self):
258260
start_id = next(node_id for node_id, node_type in node_types.items() if node_type == 'start')
259261
end_id = next(node_id for node_id, node_type in node_types.items() if node_type == 'end')
260262

263+
self.assertEqual([node['type'] for node in graph['nodes']], ['flowNode', 'flowNode', 'flowNode'])
261264
self.assertTrue(any(edge['source'] == start_id and edge['target'] == 'condition-1' for edge in graph['edges']))
262265
self.assertTrue(
263266
any(
@@ -319,8 +322,41 @@ def fake_get_current_version(flow_id):
319322
self.assertEqual(version.id, 11)
320323
scaffold_types = [node['data']['type'] for node in captured['graph_data']['nodes']]
321324
self.assertEqual(scaffold_types, ['start', 'end'])
325+
self.assertEqual([node['type'] for node in captured['graph_data']['nodes']], ['flowNode', 'flowNode'])
322326
self.assertEqual(len(captured['graph_data']['edges']), 1)
323327

328+
def test_normalize_workflow_editor_graph_rewrites_legacy_node_types(self):
329+
graph = {
330+
'nodes': [{
331+
'id': 'start-1',
332+
'type': 'start',
333+
'position': {'x': 0, 'y': 0},
334+
'data': {
335+
'id': 'start-1',
336+
'type': 'start',
337+
'name': 'Start',
338+
'group_params': [],
339+
},
340+
}, {
341+
'id': 'end-1',
342+
'type': 'end',
343+
'position': {'x': 320, 'y': 0},
344+
'data': {
345+
'id': 'end-1',
346+
'type': 'end',
347+
'name': 'End',
348+
'group_params': [],
349+
},
350+
}],
351+
'edges': [],
352+
}
353+
354+
normalized = FlowService._normalize_workflow_editor_graph(graph)
355+
356+
self.assertEqual([node['type'] for node in normalized['nodes']], ['flowNode', 'flowNode'])
357+
self.assertEqual([node['data']['type'] for node in normalized['nodes']], ['start', 'end'])
358+
self.assertEqual([node['type'] for node in graph['nodes']], ['start', 'end'])
359+
324360
def test_get_existing_external_draft_version_limits_recent_versions(self):
325361
captured = {'statements': []}
326362

@@ -554,6 +590,7 @@ def fake_update_version(version_info):
554590
self.assertEqual(len(persisted), 1)
555591
self.assertEqual(len(persisted[0]['nodes']), 2)
556592
self.assertEqual(persisted[0]['nodes'][1]['id'], node_id)
593+
self.assertEqual(persisted[0]['nodes'][1]['type'], 'flowNode')
557594
self.assertEqual(persisted[0]['nodes'][1]['position'], {'x': 120, 'y': 260})
558595
self.assertEqual(persisted[0]['nodes'][1]['data']['type'], 'code')
559596
self.assertEqual(persisted[0]['nodes'][1]['data']['name'], 'Code Node')

0 commit comments

Comments
 (0)