Duplicating a copy names it after the original; a failed duplicate or delete says why

This commit is contained in:
2026-11-01 10:59:36 +01:00
parent 8f4c7d8ff1
commit 7f40ea62de
3 changed files with 25 additions and 12 deletions
+8 -5
View File
@@ -26,7 +26,13 @@ from pydantic import BaseModel, Field, field_validator, model_validator
from fluksio.flow.messages import DType, qualify
from fluksio.flow.schemas import FlowDef, _validate_name
from fluksio.flow.store import FlowStore, StaleVersion, _updated_at, checked
from fluksio.flow.store import (
FlowStore,
StaleVersion,
_updated_at,
checked,
copy_name,
)
#: Sibling of the shared-node library, and likewise not a flow.
DASHBOARD_DIR = "_dashboards"
@@ -854,10 +860,7 @@ class DashboardStore:
"""
with self._lock, self.flows._write_lock:
source = self.read(name, draft=True)
new_name, count = f"{name}_copy", 1
while self.exists(new_name):
count += 1
new_name = f"{name}_copy_{count}"
new_name = copy_name(name, self.exists)
copy = source.model_copy(
update={
"name": new_name,
+13 -5
View File
@@ -16,10 +16,11 @@ from __future__ import annotations
import json
import logging
import re
import shutil
import subprocess
import threading
from collections.abc import Iterable
from collections.abc import Callable, Iterable
from datetime import UTC, datetime
from pathlib import Path
@@ -134,6 +135,16 @@ def _same_content(left: FlowDef, right: FlowDef) -> bool:
)
def copy_name(name: str, taken: Callable[[str], bool]) -> str:
"""``{name}_copy``, then ``_copy_2`` and on, from a copy's own original."""
base = re.sub(r"_copy(_\d+)?$", "", name)
new_name, count = f"{base}_copy", 1
while taken(new_name):
count += 1
new_name = f"{base}_copy_{count}"
return new_name
class FlowStore:
"""Reads and writes flows, committing every change."""
@@ -698,12 +709,9 @@ class FlowStore:
"""
with self._write_lock:
source = self.read_flow(name, draft=True)
new_name, count = f"{name}_copy", 1
# The directory rather than `exists`: anything left in it would
# otherwise end up in the copy.
while self._flow_dir(new_name).exists():
count += 1
new_name = f"{name}_copy_{count}"
new_name = copy_name(name, lambda n: self._flow_dir(n).exists())
copy = source.model_copy(
update={
@@ -3,6 +3,7 @@ import { Check, Copy, Plus, Search, Trash2 } from "lucide-react"
import { motion } from "motion/react"
import { type ReactNode, useRef, useState } from "react"
import type { ApiError } from "@/client"
import { Button } from "@/components/ui/button"
import {
Dialog,
@@ -22,6 +23,7 @@ import {
} from "@/components/ui/tooltip"
import useCustomToast from "@/hooks/useCustomToast"
import { transitions } from "@/lib/motion"
import { extractErrorMessage } from "@/utils"
/**
* The bar over the flows and dashboards lists: find one, publish what is
@@ -233,8 +235,8 @@ function useBatch(
for (const name of names) {
try {
await act(name)
} catch {
failed.push(name)
} catch (err) {
failed.push(`${name} (${extractErrorMessage(err as ApiError)})`)
}
}
return failed