fix(i18n): translate backend errors at UI boundaries
This commit is contained in:
1 parent
4422ac8e01
commit
2f44d36018
82 files changed
+1862
-335
No files matched your search
@@ -0,0 +1,55 @@
|
||||
import { shallowRef } from "vue";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { TreeNode } from "@/types/database";
|
||||
|
||||
const toastMock = vi.hoisted(() => vi.fn());
|
||||
const apiMock = vi.hoisted(() => ({
|
||||
buildTableSelectSql: vi.fn(async () => 'SELECT * FROM "main"."users" LIMIT 10000'),
|
||||
executeQuery: vi.fn(),
|
||||
exportQueryResultJson: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/backend/api", () => apiMock);
|
||||
vi.mock("@/lib/backend/tauriRuntime", () => ({ isTauriRuntime: () => false }));
|
||||
vi.mock("@/composables/useToast", () => ({ useToast: () => ({ toast: toastMock }) }));
|
||||
vi.mock("@/composables/useExportTracker", () => ({ useExportTracker: () => ({ addTask: vi.fn() }) }));
|
||||
vi.mock("vue-i18n", () => ({
|
||||
useI18n: () => ({
|
||||
t: (key: string, params?: Record<string, unknown>) => {
|
||||
if (key === "editor.duckdbDraining") return "上一个 DuckDB 查询仍在停止中,请稍后重试。";
|
||||
if (key === "grid.exportFailed") return `导出失败:${params?.message}`;
|
||||
return key;
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
import { useSidebarTreeExportRuntime } from "@/composables/useSidebarTreeExportRuntime";
|
||||
|
||||
describe("useSidebarTreeExportRuntime", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("translates direct executeQuery errors for sidebar JSON export", async () => {
|
||||
apiMock.executeQuery.mockRejectedValueOnce(new Error("The previous DuckDB query is still stopping. Please try again shortly."));
|
||||
const activeNode = shallowRef({ id: "table-1", type: "table", label: "users", connectionId: "conn-1", database: "db", schema: "main", children: [] } as TreeNode);
|
||||
const connectionStore = {
|
||||
ensureConnected: vi.fn(),
|
||||
getConfig: vi.fn(() => ({ db_type: "duckdb" })),
|
||||
connectionIdentifierQuote: vi.fn(() => '"'),
|
||||
treeNodes: [],
|
||||
selectedTreeNodeIds: [],
|
||||
};
|
||||
const runtime = useSidebarTreeExportRuntime({
|
||||
activeNode,
|
||||
connectionStore: connectionStore as never,
|
||||
settingsStore: {} as never,
|
||||
acceptedSelectionIds: () => null,
|
||||
});
|
||||
|
||||
await runtime.exportData("json");
|
||||
|
||||
expect(apiMock.executeQuery).toHaveBeenCalledOnce();
|
||||
expect(toastMock).toHaveBeenCalledWith("导出失败:上一个 DuckDB 查询仍在停止中,请稍后重试。", 5000);
|
||||
});
|
||||
});
|
||||
@@ -13,6 +13,7 @@ import { assessProductionSql, productionContextForDatabase } from "@/lib/databas
|
||||
import type { ColumnInfo, DatabaseType } from "@/types/database";
|
||||
import { DBX_NEO4J_ELEMENT_ID_COLUMN, usesSyntheticRowIdKey } from "@/lib/table/tableEditing";
|
||||
import { effectiveDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
|
||||
import i18n from "@/i18n";
|
||||
|
||||
interface RowItem {
|
||||
id: number;
|
||||
@@ -1273,7 +1274,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
|
||||
if (!confirmed) return;
|
||||
}
|
||||
if (customHandler && snapshot.newRows.length > 0 && customHandler.supportsInsert !== true && customHandler.canInsert !== true) {
|
||||
saveError.value = "当前保存目标不支持新增行。";
|
||||
saveError.value = i18n.global.t("grid.insertRowsNotSupported");
|
||||
return;
|
||||
}
|
||||
saveError.value = "";
|
||||
|
||||
@@ -23,6 +23,7 @@ import type { QueryResultExportRequest } from "@/lib/backend/api";
|
||||
import { usesSyntheticRowIdKey } from "@/lib/table/tableEditing";
|
||||
import { buildXlsxSqlWorksheet } from "@/lib/export/xlsxSqlSheet";
|
||||
import { formatTemporalRowsForExport } from "@/lib/dataGrid/columnFormatter";
|
||||
import { translateBackendError } from "@/i18n/backend-errors";
|
||||
|
||||
/**
|
||||
* Format metadata for backend table exports. Each entry maps a format key
|
||||
@@ -571,7 +572,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
errorMessage: e?.message || String(e),
|
||||
};
|
||||
}
|
||||
toast(t("grid.exportFailed", { message: e?.message || String(e) }), 5000);
|
||||
toast(t("grid.exportFailed", { message: translateBackendError(t, e) }), 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -593,7 +594,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
await api.exportQueryResultCsv(outputPath, result.columns, result.rows);
|
||||
toast(t("grid.exported"));
|
||||
} catch (e: any) {
|
||||
toast(t("grid.exportFailed", { message: e?.message || String(e) }), 5000);
|
||||
toast(t("grid.exportFailed", { message: translateBackendError(t, e) }), 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -617,7 +618,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
await api.exportQueryResultJson(outputPath, result.columns, result.rows);
|
||||
toast(t("grid.exported"));
|
||||
} catch (e: any) {
|
||||
toast(t("grid.exportFailed", { message: e?.message || String(e) }), 5000);
|
||||
toast(t("grid.exportFailed", { message: translateBackendError(t, e) }), 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -639,7 +640,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
await api.exportQueryResultJson(outputPath, result.columns, result.rows);
|
||||
toast(t("grid.exported"));
|
||||
} catch (e: any) {
|
||||
toast(t("grid.exportFailed", { message: e?.message || String(e) }), 5000);
|
||||
toast(t("grid.exportFailed", { message: translateBackendError(t, e) }), 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -663,7 +664,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
await api.exportQueryResultMarkdown(outputPath, result.columns, result.rows);
|
||||
toast(t("grid.exported"));
|
||||
} catch (e: any) {
|
||||
toast(t("grid.exportFailed", { message: e?.message || String(e) }), 5000);
|
||||
toast(t("grid.exportFailed", { message: translateBackendError(t, e) }), 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -685,7 +686,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
await api.exportQueryResultMarkdown(outputPath, result.columns, result.rows);
|
||||
toast(t("grid.exported"));
|
||||
} catch (e: any) {
|
||||
toast(t("grid.exportFailed", { message: e?.message || String(e) }), 5000);
|
||||
toast(t("grid.exportFailed", { message: translateBackendError(t, e) }), 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -700,7 +701,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
await saveTextFile(content, exportFileName(tableMeta.value?.tableName || "export", "txt", { preferFallback: true }), "Text", "txt");
|
||||
toast(t("grid.exported"));
|
||||
} catch (e: any) {
|
||||
toast(t("grid.exportFailed", { message: e?.message || String(e) }), 5000);
|
||||
toast(t("grid.exportFailed", { message: translateBackendError(t, e) }), 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -713,7 +714,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
await saveTextFile(content, exportFileName("export-page", "txt", { page: true }), "Text", "txt");
|
||||
toast(t("grid.exported"));
|
||||
} catch (e: any) {
|
||||
toast(t("grid.exportFailed", { message: e?.message || String(e) }), 5000);
|
||||
toast(t("grid.exportFailed", { message: translateBackendError(t, e) }), 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -784,7 +785,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
errorMessage: e?.message || String(e),
|
||||
};
|
||||
}
|
||||
toast(t("grid.exportFailed", { message: e?.message || String(e) }), 5000);
|
||||
toast(t("grid.exportFailed", { message: translateBackendError(t, e) }), 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -814,7 +815,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
await writeXlsxResult(outputPath, result, includeSqlSheet);
|
||||
toast(t("grid.exported"));
|
||||
} catch (e: any) {
|
||||
toast(t("grid.exportFailed", { message: e?.message || String(e) }), 5000);
|
||||
toast(t("grid.exportFailed", { message: translateBackendError(t, e) }), 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -857,7 +858,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
await api.exportQueryResultsXlsx(outputPath, sqlWorksheet ? [...worksheets, sqlWorksheet] : worksheets);
|
||||
toast(t("grid.exported"));
|
||||
} catch (e: any) {
|
||||
toast(t("grid.exportFailed", { message: e?.message || String(e) }), 5000);
|
||||
toast(t("grid.exportFailed", { message: translateBackendError(t, e) }), 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1113,7 +1114,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
await saveTextFile(content, exportFileName(tableMeta.value?.tableName || "export", "sql", { preferFallback: true }), "SQL", "sql");
|
||||
toast(t("grid.exported"));
|
||||
} catch (e: any) {
|
||||
toast(t("grid.exportFailed", { message: e?.message || String(e) }), 5000);
|
||||
toast(t("grid.exportFailed", { message: translateBackendError(t, e) }), 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1134,7 +1135,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
await saveTextFile(content, exportFileName("export-page", "sql", { page: true }), "SQL", "sql");
|
||||
toast(t("grid.exported"));
|
||||
} catch (e: any) {
|
||||
toast(t("grid.exportFailed", { message: e?.message || String(e) }), 5000);
|
||||
toast(t("grid.exportFailed", { message: translateBackendError(t, e) }), 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ export function useSidebarConnectionMutationRuntime(options: SidebarConnectionMu
|
||||
try {
|
||||
await revealPathInFileManager(path);
|
||||
} catch (error: any) {
|
||||
toast(typeof error === "string" ? error : error?.message || String(error), 5000);
|
||||
toast(translateBackendError(t, typeof error === "string" ? error : error?.message || String(error)), 5000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import { copyToClipboard } from "@/lib/common/clipboard";
|
||||
import { effectiveDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
|
||||
import { joinExportedDdls } from "@/lib/export/ddlExport";
|
||||
import { formatSqlInsert } from "@/lib/export/exportFormats";
|
||||
import { translateBackendError } from "@/i18n/backend-errors";
|
||||
import { sidebarStructureExportTargets } from "@/lib/sidebar/sidebarExportRuntime";
|
||||
import { fetchTableDataForExport } from "@/lib/table/tableDataExport";
|
||||
import { isLoadingStructurePreview, showStructureDocCopyDialog, showStructurePreviewDialog, structureDocCopyText, structureDocCopyTitle, structurePreviewDefaultFileName, structurePreviewError, structurePreviewSql, structurePreviewTitle } from "@/components/sidebar/sidebarTreeDialogState";
|
||||
@@ -246,7 +247,7 @@ export function useSidebarTreeExportRuntime(options: SidebarTreeExportRuntimeOpt
|
||||
await saveFileContent(content, `${node.label}.sql`, "SQL", "sql");
|
||||
toast(t("grid.exported"));
|
||||
} catch (error: any) {
|
||||
toast(t("grid.exportFailed", { message: error?.message || String(error) }), 5000);
|
||||
toast(t("grid.exportFailed", { message: translateBackendError(t, error) }), 5000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,14 +298,14 @@ export function useSidebarTreeExportRuntime(options: SidebarTreeExportRuntimeOpt
|
||||
currentTask.status = progress.status;
|
||||
currentTask.errorMessage = progress.errorMessage || null;
|
||||
if (progress.status === "Done") toast(t("grid.exported"));
|
||||
else if (progress.status === "Error") toast(t("grid.exportFailed", { message: progress.errorMessage || "" }), 5000);
|
||||
else if (progress.status === "Error") toast(t("grid.exportFailed", { message: translateBackendError(t, progress.errorMessage || "") }), 5000);
|
||||
});
|
||||
} catch (error: any) {
|
||||
if (task) {
|
||||
task.status = "Error";
|
||||
task.errorMessage = error?.message || String(error);
|
||||
}
|
||||
toast(t("grid.exportFailed", { message: error?.message || String(error) }), 5000);
|
||||
toast(t("grid.exportFailed", { message: translateBackendError(t, error) }), 5000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user