Add module delete + fail-fast on duplicate module name in wizard.
Delete button lives in module-detail header, refuses to delete a running module, and clears run_log history first since it doesn't cascade from module. Wizard now returns 409 on duplicate name before touching the destination, so a failed resubmit doesn't redundantly rerun CREATE TABLE / COMMENT ON on the dest. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
574ada5258
commit
d952b48a4e
@ -177,6 +177,26 @@ def list_modules() -> list[dict]:
|
||||
return [dict(r) for r in c.execute("SELECT * FROM module ORDER BY name")]
|
||||
|
||||
|
||||
class ModuleRunning(RuntimeError):
|
||||
"""Raised by delete_module when the module is currently running."""
|
||||
|
||||
|
||||
def delete_module(module_id: int) -> bool:
|
||||
"""Delete a module and its run_log history. Watermarks, hooks, and
|
||||
group_member rows cascade via FK. Refuses to delete if the module
|
||||
is currently running."""
|
||||
with db.connect() as c:
|
||||
row = c.execute("SELECT running FROM module WHERE id=?",
|
||||
(module_id,)).fetchone()
|
||||
if row is None:
|
||||
return False
|
||||
if row[0]:
|
||||
raise ModuleRunning(f"module id={module_id} is currently running")
|
||||
c.execute("DELETE FROM run_log WHERE module_id=?", (module_id,))
|
||||
cur = c.execute("DELETE FROM module WHERE id=?", (module_id,))
|
||||
return cur.rowcount > 0
|
||||
|
||||
|
||||
def set_next_resolved_query(module_id: int, sql: str) -> None:
|
||||
with db.connect() as c:
|
||||
c.execute("UPDATE module SET next_resolved_query=?, "
|
||||
|
||||
@ -131,6 +131,17 @@ def module_detail(request: Request, module_id: int):
|
||||
)
|
||||
|
||||
|
||||
@_router.post("/modules/{module_id}/delete")
|
||||
def module_delete(module_id: int):
|
||||
if repo.get_module(module_id) is None:
|
||||
raise HTTPException(404, f"module id={module_id} not found")
|
||||
try:
|
||||
repo.delete_module(module_id)
|
||||
except repo.ModuleRunning as e:
|
||||
raise HTTPException(409, str(e))
|
||||
return RedirectResponse(url="/", status_code=303)
|
||||
|
||||
|
||||
@_router.post("/modules/{module_id}/run")
|
||||
async def module_run_action(module_id: int, request: Request):
|
||||
form = await request.form()
|
||||
@ -293,6 +304,10 @@ async def wizard_create(request: Request):
|
||||
dest_description = (form.get("dest_description") or "").strip() or None
|
||||
picked = form.getlist("col")
|
||||
|
||||
if repo.get_module_by_name(module_name) is not None:
|
||||
raise HTTPException(
|
||||
409, f"module name {module_name!r} already exists — pick another")
|
||||
|
||||
src_conn = repo.get_connection(source_connection_id)
|
||||
if src_conn is None:
|
||||
raise HTTPException(404, f"connection id={source_connection_id} not found")
|
||||
|
||||
@ -19,6 +19,10 @@
|
||||
<input type="hidden" name="dry_run" value="1">
|
||||
<button type="submit">Dry run</button>
|
||||
</form>
|
||||
<form class="inline" method="post" action="/modules/{{ module.id }}/delete"
|
||||
onsubmit="return confirm('Delete module {{ module.name }}? This removes the module and its run history. The dest table is NOT dropped.')">
|
||||
<button type="submit" class="ghost" style="color:var(--danger)">Delete</button>
|
||||
</form>
|
||||
</span>
|
||||
</header>
|
||||
<div class="body">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user