Changesets: staged deletes of gone records count as done, name stale ops
A web changeset with a staged record delete could not be applied after the record was deleted elsewhere (e.g. with the CLI), and the error only said "record N does not exist", so it was hard to find the operation to remove. A delete of a record that is already gone is now a no-op with a warning. Other operations that no longer apply fail with their number and summary (op_summary in the API), and the Changes page marks that row and offers to remove it from the error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c37de31f4c
commit
49b1c872c9
7 files changed
+113
-17
No files matched your search
@@ -44,18 +44,33 @@ async function draftPanel(d) {
|
||||
const applyBtn = h("button", { class: "primary", disabled: true }, "Apply changes");
|
||||
const forceBox = h("label", { class: "check small hidden" }, h("input", { type: "checkbox" }), "Overwrite files changed outside Jarl");
|
||||
const ops = d.ops || [];
|
||||
const removeOp = async (seq) => {
|
||||
try {
|
||||
const cs = await api("DELETE", "/changesets/" + d.id + "/ops/" + seq);
|
||||
useDraft(cs);
|
||||
rerender();
|
||||
} catch (e) { toast(e.message, "err", 8000); }
|
||||
};
|
||||
const opsTable = table([
|
||||
{ label: "#", render: (o) => String(o.seq) },
|
||||
{ label: "Change", render: (o) => o.summary || o.op, tdClass: "mono small" },
|
||||
{ label: "Added", render: (o) => time(o.added_at) },
|
||||
{ label: "", render: (o) => h("button", { class: "small", title: "Remove this operation from the changeset", onclick: async () => {
|
||||
try {
|
||||
const cs = await api("DELETE", "/changesets/" + d.id + "/ops/" + o.seq);
|
||||
useDraft(cs);
|
||||
rerender();
|
||||
} catch (e) { toast(e.message, "err", 8000); }
|
||||
} }, "Remove"), tdClass: "actions-cell" },
|
||||
{ label: "", render: (o) => h("button", { class: "small", title: "Remove this operation from the changeset", onclick: () => removeOp(o.seq) }, "Remove"), tdClass: "actions-cell" },
|
||||
], ops);
|
||||
// failedOp marks the operation that no longer applies and says how to
|
||||
// get past it.
|
||||
const failedOp = (err) => {
|
||||
const i = ops.findIndex((o) => o.seq === err.op);
|
||||
const rows = opsTable.querySelectorAll("tbody tr");
|
||||
if (i < 0 || !rows[i]) return errorBox({ message: err.message, details: err });
|
||||
rows[i].classList.add("op-failed");
|
||||
return h("div", { class: "msg err" },
|
||||
h("div", null, h("strong", null, "Operation " + err.op + " can't be applied: "), err.message),
|
||||
h("div", { class: "mono small" }, ops[i].summary || ops[i].op),
|
||||
h("div", { style: { marginTop: "8px" } },
|
||||
h("button", { class: "small", onclick: () => removeOp(err.op) }, "Remove operation " + err.op),
|
||||
" ", h("span", { class: "muted small" }, "It is marked in the list above. Then check the rest again.")));
|
||||
};
|
||||
const runPreview = async () => {
|
||||
try {
|
||||
const p = await get("/changesets/" + d.id + "/preview");
|
||||
@@ -64,7 +79,7 @@ async function draftPanel(d) {
|
||||
const drift = (p.drift || []);
|
||||
forceBox.classList.toggle("hidden", drift.length === 0);
|
||||
mount(preview,
|
||||
p.error ? errorBox({ message: p.error.message, details: p.error }) : null,
|
||||
p.error ? (p.error.op ? failedOp(p.error) : errorBox({ message: p.error.message, details: p.error })) : null,
|
||||
valid ? h("div", { class: "msg ok" }, "Valid. named-checkconf accepts the new configuration.", (p.serials || []).length ? " Serials: " + p.serials.map((s) => s.zone.replace(/\.$/, "") + " " + s.old + "→" + s.new).join(", ") : "") : null,
|
||||
!p.error && !valid ? h("div", { class: "msg err" }, "This changeset can't be applied yet:") : null,
|
||||
problemsList(p.problems),
|
||||
|
||||
Reference in new issue
Block a user