fix(mq): show clear-backlog errors inside the confirm dialog
This commit is contained in:
1 parent
4bac6b56de
commit
d36c094c4f
2 files changed
+108
-6
No files matched your search
@@ -60,6 +60,7 @@ const showDeleteDialog = ref(false);
|
||||
const deleting = ref(false);
|
||||
const clearBacklogTarget = ref<SubscriptionInfo>();
|
||||
const showClearBacklogDialog = ref(false);
|
||||
const clearBacklogError = ref<string>();
|
||||
const clearingBacklog = ref(false);
|
||||
|
||||
const formData = ref({
|
||||
@@ -440,6 +441,7 @@ function handleClearBacklog(sub: SubscriptionInfo) {
|
||||
return;
|
||||
}
|
||||
clearBacklogTarget.value = sub;
|
||||
clearBacklogError.value = undefined;
|
||||
showClearBacklogDialog.value = true;
|
||||
}
|
||||
|
||||
@@ -450,13 +452,13 @@ async function confirmClearBacklog() {
|
||||
const topicRef = getPulsarTopicRef();
|
||||
if (!topicRef) return;
|
||||
clearingBacklog.value = true;
|
||||
error.value = undefined;
|
||||
clearBacklogError.value = undefined;
|
||||
try {
|
||||
await mqClearBacklog(props.connectionId, topicRef, sub.name);
|
||||
showClearBacklogDialog.value = false;
|
||||
await loadSubscriptions();
|
||||
} catch (e: unknown) {
|
||||
error.value = formatError(e);
|
||||
clearBacklogError.value = formatError(e);
|
||||
} finally {
|
||||
clearingBacklog.value = false;
|
||||
}
|
||||
@@ -829,7 +831,11 @@ onBeforeUnmount(() => {
|
||||
:loading="clearingBacklog"
|
||||
:close-on-confirm="false"
|
||||
@confirm="confirmClearBacklog"
|
||||
/>
|
||||
>
|
||||
<template #options>
|
||||
<div v-if="clearBacklogError" class="form-error" role="alert">{{ clearBacklogError }}</div>
|
||||
</template>
|
||||
</DangerConfirmDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -27,7 +27,17 @@ vi.mock("@/composables/useMqMutationGuard", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("@/components/editor/DangerConfirmDialog.vue", () => ({
|
||||
default: { template: "<div />" },
|
||||
default: {
|
||||
props: ["open", "title", "confirmLabel", "loading", "closeOnConfirm"],
|
||||
emits: ["update:open", "confirm"],
|
||||
template: `
|
||||
<div v-if="open" role="dialog" :aria-label="title">
|
||||
<slot name="options" />
|
||||
<button :disabled="loading" @click="$emit('update:open', false)">dangerDialog.cancel</button>
|
||||
<button :disabled="loading" @click="closeOnConfirm !== false && $emit('update:open', false); $emit('confirm')">{{ confirmLabel }}</button>
|
||||
</div>
|
||||
`,
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("@/components/mq/rocketmq/RocketMqConsumerGroupDialogs.vue", () => ({
|
||||
@@ -66,10 +76,12 @@ function buttonWithExactText(container: ParentNode, text: string): HTMLButtonEle
|
||||
|
||||
function deferred<T>() {
|
||||
let resolve!: (value: T) => void;
|
||||
const promise = new Promise<T>((resolvePromise) => {
|
||||
let reject!: (reason: unknown) => void;
|
||||
const promise = new Promise<T>((resolvePromise, rejectPromise) => {
|
||||
resolve = resolvePromise;
|
||||
reject = rejectPromise;
|
||||
});
|
||||
return { promise, resolve };
|
||||
return { promise, resolve, reject };
|
||||
}
|
||||
|
||||
async function mountPanel(overrides: Record<string, unknown> = {}) {
|
||||
@@ -82,6 +94,7 @@ async function mountPanel(overrides: Record<string, unknown> = {}) {
|
||||
namespace: "default",
|
||||
mqSystemKind: "kafka",
|
||||
supportsResetCursor: true,
|
||||
supportsClearBacklog: true,
|
||||
supportsPeekMessages: true,
|
||||
...overrides,
|
||||
});
|
||||
@@ -205,6 +218,89 @@ describe("SubscriptionsPanel message peek", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("SubscriptionsPanel clear backlog", () => {
|
||||
async function openClearBacklog(panel: HTMLDivElement) {
|
||||
buttonWithExactText(panel, "mqSubscriptions.clearBacklog").click();
|
||||
await flushUi();
|
||||
const dialog = panel.querySelector('[role="dialog"][aria-label="mqSubscriptions.clearBacklog"]');
|
||||
expect(dialog).not.toBeNull();
|
||||
return dialog!;
|
||||
}
|
||||
|
||||
it("keeps a failed clear backlog open with the error in the dialog, not the panel", async () => {
|
||||
backend.mqClearBacklog.mockRejectedValueOnce(new Error("Cannot clear an active consumer group"));
|
||||
const panel = await mountPanel();
|
||||
const dialog = await openClearBacklog(panel);
|
||||
|
||||
buttonWithExactText(dialog, "mqSubscriptions.clearBacklog").click();
|
||||
await vi.waitFor(() => expect(dialog.querySelector(".form-error")?.textContent).toContain("Cannot clear an active consumer group"));
|
||||
|
||||
expect(backend.mqClearBacklog).toHaveBeenCalledExactlyOnceWith("mq-1", { tenant: "_kafka", namespace: "default", topic: "events", persistent: true, partitioned: false }, "orders-consumer");
|
||||
expect(dialog.isConnected).toBe(true);
|
||||
expect(buttonWithExactText(dialog, "mqSubscriptions.clearBacklog").disabled).toBe(false);
|
||||
expect(panel.querySelector(".panel-error")).toBeNull();
|
||||
expect(panel.querySelector(".subscriptions-table")?.textContent).toContain("orders-consumer");
|
||||
expect(backend.mqListSubscriptions).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("clears the previous error when the dialog is closed and reopened", async () => {
|
||||
backend.mqClearBacklog.mockRejectedValueOnce(new Error("Previous clear backlog failed"));
|
||||
const panel = await mountPanel();
|
||||
const dialog = await openClearBacklog(panel);
|
||||
buttonWithExactText(dialog, "mqSubscriptions.clearBacklog").click();
|
||||
await vi.waitFor(() => expect(dialog.textContent).toContain("Previous clear backlog failed"));
|
||||
|
||||
buttonWithExactText(dialog, "dangerDialog.cancel").click();
|
||||
await flushUi();
|
||||
expect(dialog.isConnected).toBe(false);
|
||||
|
||||
const reopenedDialog = await openClearBacklog(panel);
|
||||
expect(reopenedDialog.querySelector(".form-error")).toBeNull();
|
||||
expect(panel.textContent).not.toContain("Previous clear backlog failed");
|
||||
expect(panel.querySelector(".panel-error")).toBeNull();
|
||||
});
|
||||
|
||||
it.each(["success", "failure"])("clears the previous error while retrying and handles retry %s", async (outcome) => {
|
||||
const retry = deferred<void>();
|
||||
backend.mqClearBacklog.mockRejectedValueOnce(new Error("First attempt failed")).mockReturnValueOnce(retry.promise);
|
||||
const panel = await mountPanel();
|
||||
const dialog = await openClearBacklog(panel);
|
||||
buttonWithExactText(dialog, "mqSubscriptions.clearBacklog").click();
|
||||
await vi.waitFor(() => expect(dialog.textContent).toContain("First attempt failed"));
|
||||
|
||||
buttonWithExactText(dialog, "mqSubscriptions.clearBacklog").click();
|
||||
await vi.waitFor(() => expect(backend.mqClearBacklog).toHaveBeenCalledTimes(2));
|
||||
expect(dialog.querySelector(".form-error")).toBeNull();
|
||||
expect(dialog.isConnected).toBe(true);
|
||||
expect(buttonWithExactText(dialog, "mqSubscriptions.clearBacklog").disabled).toBe(true);
|
||||
|
||||
if (outcome === "success") {
|
||||
backend.mqListSubscriptions.mockResolvedValue([{ ...subscription("orders-consumer", "shared"), msgBacklog: 123 }]);
|
||||
retry.resolve();
|
||||
await vi.waitFor(() => {
|
||||
expect(dialog.isConnected).toBe(false);
|
||||
expect(backend.mqListSubscriptions).toHaveBeenCalledTimes(2);
|
||||
expect(panel.querySelector(".subscriptions-table")?.textContent).toContain("123");
|
||||
});
|
||||
} else {
|
||||
retry.reject(new Error("Retry failed"));
|
||||
await vi.waitFor(() => expect(dialog.querySelector(".form-error")?.textContent).toContain("Retry failed"));
|
||||
expect(dialog.isConnected).toBe(true);
|
||||
expect(dialog.textContent).not.toContain("First attempt failed");
|
||||
expect(backend.mqListSubscriptions).toHaveBeenCalledTimes(1);
|
||||
}
|
||||
expect(panel.querySelector(".panel-error")).toBeNull();
|
||||
});
|
||||
|
||||
it("keeps subscription list loading failures in the panel", async () => {
|
||||
backend.mqListSubscriptions.mockRejectedValueOnce(new Error("Subscription list failed"));
|
||||
const panel = await mountPanel();
|
||||
|
||||
await vi.waitFor(() => expect(panel.querySelector(".panel-error")?.textContent).toContain("Subscription list failed"));
|
||||
expect(panel.querySelector('[role="dialog"]')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("SubscriptionsPanel Kafka absolute offset reset", () => {
|
||||
async function openAbsoluteReset(panel: HTMLDivElement) {
|
||||
await vi.waitFor(() => {
|
||||
|
||||
Reference in new issue
Block a user