feat: maintenance flow UX redesign — batch hub, context strip, run history upgrades #89
@@ -170,15 +170,20 @@ async def sync_steps_from_tree(
|
||||
# Soft-delete previously synced steps that no longer exist in the tree
|
||||
current_node_ids = [s["source_node_id"] for s in extracted]
|
||||
if current_node_ids:
|
||||
# Build NOT IN using explicit named placeholders — asyncpg does not
|
||||
# auto-cast a Python list to a PostgreSQL array in text() queries.
|
||||
placeholders = ", ".join(f":id_{i}" for i in range(len(current_node_ids)))
|
||||
params = {f"id_{i}": nid for i, nid in enumerate(current_node_ids)}
|
||||
params.update({"tree_id": str(tree_id), "now": now})
|
||||
await db.execute(
|
||||
text("""
|
||||
text(f"""
|
||||
UPDATE step_library
|
||||
SET is_active = false, updated_at = :now
|
||||
WHERE source_tree_id = :tree_id
|
||||
AND is_flow_synced = true
|
||||
AND source_node_id != ALL(:node_ids)
|
||||
AND source_node_id NOT IN ({placeholders})
|
||||
"""),
|
||||
{"tree_id": str(tree_id), "node_ids": current_node_ids, "now": now}
|
||||
params
|
||||
)
|
||||
else:
|
||||
await db.execute(
|
||||
@@ -194,7 +199,11 @@ async def sync_steps_from_tree(
|
||||
|
||||
|
||||
async def deactivate_synced_steps_for_tree(db: AsyncSession, tree_id: UUID) -> None:
|
||||
"""Soft-delete all synced library entries for a tree (on tree delete/deactivate)."""
|
||||
"""Soft-delete all synced library entries for a tree (on tree delete/deactivate).
|
||||
|
||||
Must be called BEFORE deleting the tree row — after deletion the FK ondelete='SET NULL'
|
||||
will null source_tree_id, making the WHERE clause match nothing.
|
||||
"""
|
||||
await db.execute(
|
||||
text("""
|
||||
UPDATE step_library
|
||||
|
||||
Reference in New Issue
Block a user