fix(errors): unify backend error protocol and preserve db detail
This commit is contained in:
1 parent
f1617bbd87
commit
c8c40ba839
24 files changed
+1197
-182
No files matched your search
@@ -0,0 +1,72 @@
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
import { backendResponseError, importAgentDriver, installJdbcPluginLocal } from "@/lib/backend/http";
|
||||
import { BackendErrorException } from "@/lib/backend/errorUtils";
|
||||
|
||||
const envelope = {
|
||||
version: 1,
|
||||
code: "DBX-JDBC-4001",
|
||||
messageKey: "backendErrors.jdbc.sqlFailed",
|
||||
messageParams: { stage: "execute" },
|
||||
source: "jdbcAgent",
|
||||
operationOutcome: "unknown",
|
||||
detail: "Incorrect syntax near SELECT",
|
||||
} as const;
|
||||
|
||||
describe("HTTP backend error parsing", () => {
|
||||
test.each([
|
||||
["direct envelope", JSON.stringify(envelope), envelope],
|
||||
["nested envelope", JSON.stringify({ error: envelope }), envelope],
|
||||
["legacy text", "relation missing_table does not exist", undefined],
|
||||
["malformed JSON text", "{not-json", undefined],
|
||||
])("preserves %s body diagnostics", async (_name, body, expected) => {
|
||||
const error = await backendResponseError(new Response(body, { status: 500 }));
|
||||
if (expected) {
|
||||
expect(error.backendError).toEqual(expected);
|
||||
} else {
|
||||
expect(error.backendError.code).toBe("DBX-LEGACY-0001");
|
||||
expect(error.backendError.detail).toBe(body);
|
||||
}
|
||||
});
|
||||
|
||||
test("uses a stable summary for an empty body", async () => {
|
||||
const error = await backendResponseError(new Response("", { status: 503 }));
|
||||
expect(error.backendError.code).toBe("DBX-LEGACY-0001");
|
||||
expect(error.backendError.detail).toBeUndefined();
|
||||
expect(error.message).toBe("Backend request failed");
|
||||
});
|
||||
|
||||
test("keeps a safe SQL diagnostic in a JSON envelope unchanged", async () => {
|
||||
const error = await backendResponseError(new Response(JSON.stringify(envelope), { status: 400 }));
|
||||
expect(error.backendError.detail).toBe("Incorrect syntax near SELECT");
|
||||
});
|
||||
|
||||
test.each([
|
||||
["JDBC plugin upload", () => installJdbcPluginLocal(new File(["plugin"], "plugin.zip"))],
|
||||
["Agent driver upload", () => importAgentDriver("postgres", new File(["driver"], "driver.zip"))],
|
||||
])("normalizes %s multipart failures through the nested backend envelope", async (_name, upload) => {
|
||||
vi.stubGlobal("fetch", vi.fn().mockResolvedValue(new Response(JSON.stringify({ error: envelope }), { status: 400 })));
|
||||
|
||||
const error = await upload().catch((value: unknown) => value);
|
||||
expect(error).toBeInstanceOf(BackendErrorException);
|
||||
expect(error).toMatchObject({
|
||||
backendError: expect.objectContaining({ code: envelope.code, detail: envelope.detail }),
|
||||
});
|
||||
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
test.each([
|
||||
["JDBC plugin upload", () => installJdbcPluginLocal(new File(["plugin"], "plugin.zip"))],
|
||||
["Agent driver upload", () => importAgentDriver("postgres", new File(["driver"], "driver.zip"))],
|
||||
])("normalizes %s multipart failures through the direct backend envelope", async (_name, upload) => {
|
||||
vi.stubGlobal("fetch", vi.fn().mockResolvedValue(new Response(JSON.stringify(envelope), { status: 400 })));
|
||||
|
||||
const error = await upload().catch((value: unknown) => value);
|
||||
expect(error).toBeInstanceOf(BackendErrorException);
|
||||
expect(error).toMatchObject({
|
||||
backendError: expect.objectContaining({ code: envelope.code, detail: envelope.detail }),
|
||||
});
|
||||
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
});
|
||||
@@ -9,13 +9,21 @@ export interface BackendError {
|
||||
code: string;
|
||||
messageKey: string;
|
||||
messageParams: Record<string, BackendErrorParam>;
|
||||
source: "jdbcAgent" | "jdbcAgentLegacy" | "legacyBackend";
|
||||
/** Compatibility provenance. New callers should prefer origin metadata. */
|
||||
source: string;
|
||||
operationOutcome: "not_started" | "unknown";
|
||||
origin?: {
|
||||
subsystem: string;
|
||||
adapter: string;
|
||||
driver?: string;
|
||||
};
|
||||
detail?: string;
|
||||
diagnostics?: Record<string, unknown>;
|
||||
helpUrl?: string;
|
||||
}
|
||||
|
||||
const MAX_FALLBACK_CHARS = 64 * 1024;
|
||||
const MAX_ERROR_PARSE_DEPTH = 16;
|
||||
const AGENT_RPC_ERROR_DATA_MARKER = "\nDBX_AGENT_ERROR_DATA:";
|
||||
|
||||
export function sanitizeBackendErrorMessage(message: string): string {
|
||||
@@ -45,25 +53,75 @@ function isBackendError(value: unknown): value is BackendError {
|
||||
!candidate.messageParams ||
|
||||
typeof candidate.messageParams !== "object" ||
|
||||
Array.isArray(candidate.messageParams) ||
|
||||
!["jdbcAgent", "jdbcAgentLegacy", "legacyBackend"].includes(String(candidate.source)) ||
|
||||
typeof candidate.source !== "string" ||
|
||||
candidate.source.length === 0 ||
|
||||
candidate.source.length > 64 ||
|
||||
!["not_started", "unknown"].includes(String(candidate.operationOutcome))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (candidate.origin !== undefined) {
|
||||
const origin = candidate.origin;
|
||||
const originRecord = origin as Record<string, unknown>;
|
||||
if (
|
||||
!origin ||
|
||||
typeof origin !== "object" ||
|
||||
Array.isArray(origin) ||
|
||||
typeof originRecord.subsystem !== "string" ||
|
||||
typeof originRecord.adapter !== "string" ||
|
||||
originRecord.subsystem.length > 64 ||
|
||||
originRecord.adapter.length > 64 ||
|
||||
(originRecord.driver !== undefined && (typeof originRecord.driver !== "string" || originRecord.driver.length > 64))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (candidate.detail !== undefined && typeof candidate.detail !== "string") return false;
|
||||
return Object.values(candidate.messageParams).every((param) => typeof param === "string" || typeof param === "boolean" || (typeof param === "number" && Number.isFinite(param)));
|
||||
}
|
||||
|
||||
export function normalizeBackendError(error: unknown): BackendError | null {
|
||||
if (error instanceof BackendErrorException) return error.backendError;
|
||||
return normalizeBackendErrorAtDepth(error, new WeakSet<object>(), 0);
|
||||
}
|
||||
|
||||
function normalizeBackendErrorAtDepth(error: unknown, seen: WeakSet<object>, depth: number): BackendError | null {
|
||||
if (depth > MAX_ERROR_PARSE_DEPTH) return null;
|
||||
|
||||
if (error && typeof error === "object") {
|
||||
if (seen.has(error)) return null;
|
||||
seen.add(error);
|
||||
|
||||
if ("name" in error && error.name === "BackendErrorException" && "backendError" in error) {
|
||||
const normalized = normalizeBackendErrorAtDepth((error as { backendError: unknown }).backendError, seen, depth + 1);
|
||||
if (normalized) return normalized;
|
||||
}
|
||||
}
|
||||
if (typeof error === "string") {
|
||||
try {
|
||||
return normalizeBackendErrorAtDepth(JSON.parse(error), seen, depth + 1);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (isBackendError(error)) return error;
|
||||
if (error && typeof error === "object" && "backendError" in error) {
|
||||
const backendError = (error as { backendError: unknown }).backendError;
|
||||
if (isBackendError(backendError)) return backendError;
|
||||
const normalized = normalizeBackendErrorAtDepth(backendError, seen, depth + 1);
|
||||
if (normalized) return normalized;
|
||||
}
|
||||
if (error && typeof error === "object" && "error" in error) {
|
||||
const nested = (error as { error: unknown }).error;
|
||||
if (isBackendError(nested)) return nested;
|
||||
const normalized = normalizeBackendErrorAtDepth(nested, seen, depth + 1);
|
||||
if (normalized) return normalized;
|
||||
}
|
||||
if (error && typeof error === "object" && "message" in error && typeof error.message === "string") {
|
||||
try {
|
||||
const parsed: unknown = JSON.parse(error.message);
|
||||
const normalized = normalizeBackendErrorAtDepth(parsed, seen, depth + 1);
|
||||
if (normalized) return normalized;
|
||||
} catch {
|
||||
// Keep checking compatibility wrappers before falling back to plain text.
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -73,7 +131,8 @@ export class BackendErrorException extends Error {
|
||||
|
||||
constructor(error: unknown) {
|
||||
const backendError = normalizeRawBackendError(error);
|
||||
const fallbackMessage = sanitizeBackendErrorMessage(typeof error === "string" ? error : error instanceof Error ? error.message : "Backend request failed");
|
||||
const fallbackDetail = boundedFallbackText(error);
|
||||
const fallbackMessage = sanitizeBackendErrorMessage(fallbackDetail ?? "Backend request failed");
|
||||
super(backendError?.detail ? sanitizeBackendErrorMessage(backendError.detail) : fallbackMessage);
|
||||
this.name = "BackendErrorException";
|
||||
this.backendError = backendError ?? {
|
||||
@@ -83,23 +142,46 @@ export class BackendErrorException extends Error {
|
||||
messageParams: {},
|
||||
source: "legacyBackend",
|
||||
operationOutcome: "unknown",
|
||||
detail: fallbackMessage,
|
||||
origin: { subsystem: "backend", adapter: "legacy" },
|
||||
...(fallbackDetail ? { detail: sanitizeBackendErrorMessage(fallbackDetail) } : {}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeRawBackendError(error: unknown): BackendError | null {
|
||||
if (typeof error === "string") {
|
||||
try {
|
||||
const parsed: unknown = JSON.parse(error);
|
||||
return normalizeBackendError(parsed);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return normalizeBackendError(error);
|
||||
}
|
||||
|
||||
function boundedFallbackText(error: unknown): string | undefined {
|
||||
return boundedFallbackTextAtDepth(error, new WeakSet<object>(), 0);
|
||||
}
|
||||
|
||||
function boundedFallbackTextAtDepth(error: unknown, seen: WeakSet<object>, depth: number): string | undefined {
|
||||
if (depth > MAX_ERROR_PARSE_DEPTH) return undefined;
|
||||
|
||||
let text: string | undefined;
|
||||
if (typeof error === "string") {
|
||||
text = error;
|
||||
} else if (error instanceof Error) {
|
||||
text = error.message;
|
||||
} else if (error && typeof error === "object") {
|
||||
if (seen.has(error)) return undefined;
|
||||
seen.add(error);
|
||||
const candidate = error as Record<string, unknown>;
|
||||
for (const key of ["message", "reason", "detail"]) {
|
||||
if (typeof candidate[key] === "string") {
|
||||
text = candidate[key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!text && "error" in candidate) text = boundedFallbackTextAtDepth(candidate.error, seen, depth + 1);
|
||||
if (!text && "backendError" in candidate) text = boundedFallbackTextAtDepth(candidate.backendError, seen, depth + 1);
|
||||
}
|
||||
const normalized = text?.trim();
|
||||
if (!normalized) return undefined;
|
||||
return Array.from(normalized).slice(0, MAX_FALLBACK_CHARS).join("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats an unknown error value into a human-readable string.
|
||||
* Handles Error objects, strings, null/undefined, and other types.
|
||||
@@ -117,6 +199,7 @@ function normalizeRawBackendError(error: unknown): BackendError | null {
|
||||
export function formatError(e: unknown): string {
|
||||
const backendError = normalizeBackendError(e);
|
||||
if (backendError?.detail) return sanitizeBackendErrorMessage(backendError.detail);
|
||||
if (backendError) return backendError.code;
|
||||
|
||||
if (e instanceof Error) {
|
||||
return sanitizeBackendErrorMessage(e.message);
|
||||
|
||||
@@ -251,7 +251,7 @@ async function put<T>(url: string, body: unknown): Promise<T> {
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async function backendResponseError(response: Response): Promise<BackendErrorException> {
|
||||
export async function backendResponseError(response: Response): Promise<BackendErrorException> {
|
||||
const text = await response.text();
|
||||
let payload: unknown = text;
|
||||
try {
|
||||
@@ -287,7 +287,7 @@ export async function testConnectionWithInfo(config: ConnectionConfig): Promise<
|
||||
if (response.status === 404) {
|
||||
return normalizeConnectionTestResult(await testConnection(config), config);
|
||||
}
|
||||
if (!response.ok) throw new Error(await response.text());
|
||||
if (!response.ok) throw await backendResponseError(response);
|
||||
return normalizeConnectionTestResult(await response.json(), config);
|
||||
}
|
||||
|
||||
@@ -406,7 +406,7 @@ export async function importJdbcDrivers(pathsOrFiles: (string | File)[]): Promis
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
if (!res.ok) throw await backendResponseError(res);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -455,7 +455,7 @@ export async function installJdbcPluginLocal(pathOrFile: string | File): Promise
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
if (!uploadRes.ok) throw new Error(await uploadRes.text());
|
||||
if (!uploadRes.ok) throw await backendResponseError(uploadRes);
|
||||
return uploadRes.json();
|
||||
}
|
||||
|
||||
@@ -534,7 +534,7 @@ export async function importAgentsFromZip(fileOrPath: string | File, operationId
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
if (!res.ok) throw await backendResponseError(res);
|
||||
const result: { count: number } = await res.json();
|
||||
return result.count;
|
||||
}
|
||||
@@ -556,7 +556,7 @@ export async function importAgentDriver(dbType: string, pathOrFile: string | Fil
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
if (!uploadRes.ok) throw new Error(await uploadRes.text());
|
||||
if (!uploadRes.ok) throw await backendResponseError(uploadRes);
|
||||
}
|
||||
|
||||
export const importAgentJar = importAgentDriver;
|
||||
@@ -1293,7 +1293,7 @@ export async function aiStream(sessionId: string, request: AiCompletionRequest,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ session_id: sessionId, request }),
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
if (!res.ok) throw await backendResponseError(res);
|
||||
|
||||
const reader = res.body!.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
@@ -1389,7 +1389,7 @@ export async function aiAgentStream(
|
||||
}),
|
||||
signal,
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
if (!res.ok) throw await backendResponseError(res);
|
||||
|
||||
const reader = res.body!.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
@@ -1492,7 +1492,7 @@ export async function saveMcpGlobalPolicy(policy: Omit<McpGlobalPolicy, "configu
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(policy),
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
if (!res.ok) throw await backendResponseError(res);
|
||||
}
|
||||
|
||||
export async function loadMaxAgentTurns(): Promise<number> {
|
||||
@@ -1505,7 +1505,7 @@ export async function saveMaxAgentTurns(maxAgentTurns: number): Promise<void> {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ maxAgentTurns }),
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
if (!res.ok) throw await backendResponseError(res);
|
||||
}
|
||||
|
||||
export async function loadMaxRetries(): Promise<number> {
|
||||
@@ -1518,7 +1518,7 @@ export async function saveMaxRetries(maxRetries: number): Promise<void> {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ maxRetries }),
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
if (!res.ok) throw await backendResponseError(res);
|
||||
}
|
||||
|
||||
export interface OpenTabsStatePayload {
|
||||
@@ -1820,7 +1820,7 @@ export async function previewSqlFile(fileOrPath: string | File): Promise<SqlFile
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
if (!res.ok) throw await backendResponseError(res);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1890,7 +1890,7 @@ export async function startTransfer(request: TransferRequest, onProgress: (progr
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ request }),
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
if (!res.ok) throw await backendResponseError(res);
|
||||
|
||||
// 2. SSE to listen for progress
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -1952,7 +1952,7 @@ export async function previewTableImportFile(fileOrPath: string | File | TableIm
|
||||
previewLimit: options.previewLimit,
|
||||
}),
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
if (!res.ok) throw await backendResponseError(res);
|
||||
return res.json();
|
||||
}
|
||||
const formData = new FormData();
|
||||
@@ -1964,7 +1964,7 @@ export async function previewTableImportFile(fileOrPath: string | File | TableIm
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
if (!res.ok) throw await backendResponseError(res);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1975,7 +1975,7 @@ export async function importTableFile(request: TableImportRequest, onProgress: (
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ request }),
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
if (!res.ok) throw await backendResponseError(res);
|
||||
|
||||
// 2. SSE to listen for progress
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -2029,7 +2029,7 @@ export async function exportDatabaseSql(request: DatabaseExportRequest, onProgre
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ request }),
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
if (!res.ok) throw await backendResponseError(res);
|
||||
|
||||
// 2. SSE to listen for progress
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -2691,7 +2691,7 @@ export async function nacosSearchConfigContent(connectionId: string, req: NacosC
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ connectionId, req }),
|
||||
});
|
||||
if (!response.ok) throw new Error(await response.text());
|
||||
if (!response.ok) throw await backendResponseError(response);
|
||||
if (!response.body) throw new Error("Nacos content search did not return a response stream");
|
||||
|
||||
const reader = response.body.getReader();
|
||||
@@ -2738,7 +2738,7 @@ export async function nacosExportConfigs(connectionId: string, selector: NacosCo
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ connectionId, selector, fileName }),
|
||||
});
|
||||
if (!response.ok) throw new Error(await response.text());
|
||||
if (!response.ok) throw await backendResponseError(response);
|
||||
const blob = await response.blob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
const anchor = document.createElement("a");
|
||||
@@ -2758,7 +2758,7 @@ export async function nacosPreviewConfigImport(connectionId: string, targetNames
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
if (!response.ok) throw new Error(await response.text());
|
||||
if (!response.ok) throw await backendResponseError(response);
|
||||
return response.json();
|
||||
}
|
||||
|
||||
@@ -3039,7 +3039,7 @@ export async function documentDownloadGridFsFile(connectionId: string, database:
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ connectionId, database, bucket, fileId }),
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
if (!res.ok) throw await backendResponseError(res);
|
||||
const data = (await res.json()) as number[];
|
||||
return new Uint8Array(data);
|
||||
}
|
||||
@@ -3058,7 +3058,7 @@ export async function documentUploadGridFsFile(connectionId: string, database: s
|
||||
method: "POST",
|
||||
body,
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
if (!res.ok) throw await backendResponseError(res);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,15 @@ import { BackendErrorException, type BackendError } from "@/lib/backend/errorUti
|
||||
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
|
||||
import { normalizeRustMongoCommand, type MongoCommand } from "@/lib/mongo/mongoShellCommand";
|
||||
import { ExternalSqlFileTooLargeError } from "@/lib/sql/sqlFileOpen";
|
||||
|
||||
/** Normalize Tauri rejections once at the public backend boundary. */
|
||||
async function invokeBackend<T>(command: string, args?: Record<string, unknown>): Promise<T> {
|
||||
try {
|
||||
return await invoke<T>(command, args);
|
||||
} catch (error) {
|
||||
throw error instanceof BackendErrorException ? error : new BackendErrorException(error);
|
||||
}
|
||||
}
|
||||
import type {
|
||||
ConnectionConfig,
|
||||
ConnectionTestResult,
|
||||
@@ -835,7 +844,7 @@ export async function setAiGlobalCustomInstructions(content: string): Promise<vo
|
||||
}
|
||||
|
||||
export async function testConnection(config: ConnectionConfig): Promise<string> {
|
||||
return invoke("test_connection", { config });
|
||||
return invokeBackend("test_connection", { config });
|
||||
}
|
||||
|
||||
export async function testConnectionWithInfo(config: ConnectionConfig): Promise<ConnectionTestResult> {
|
||||
@@ -851,31 +860,31 @@ export async function testConnectionWithInfo(config: ConnectionConfig): Promise<
|
||||
}
|
||||
|
||||
export async function connectDb(config: ConnectionConfig, clientAttempt?: number): Promise<string> {
|
||||
return invoke("connect_db", { config, clientAttempt });
|
||||
return invokeBackend("connect_db", { config, clientAttempt });
|
||||
}
|
||||
|
||||
export async function connectionDatabaseInfo(connectionId: string, database?: string): Promise<DatabaseConnectionInfo | undefined> {
|
||||
const info = await invoke<DatabaseConnectionInfo | null>("connection_database_info", { connectionId, database });
|
||||
const info = await invokeBackend<DatabaseConnectionInfo | null>("connection_database_info", { connectionId, database });
|
||||
return info ?? undefined;
|
||||
}
|
||||
|
||||
export async function saveConnectionDatabaseInfo(connectionId: string, databaseInfo: DatabaseConnectionInfo): Promise<void> {
|
||||
return invoke("save_connection_database_info", {
|
||||
return invokeBackend("save_connection_database_info", {
|
||||
connectionId,
|
||||
databaseInfo,
|
||||
});
|
||||
}
|
||||
|
||||
export async function connectionFinalProxyPort(config: ConnectionConfig): Promise<number> {
|
||||
return invoke("connection_final_proxy_port", { config });
|
||||
return invokeBackend("connection_final_proxy_port", { config });
|
||||
}
|
||||
|
||||
export async function disconnectDb(connectionId: string, clientAttempt?: number): Promise<void> {
|
||||
return invoke("disconnect_db", { connectionId, clientAttempt });
|
||||
return invokeBackend("disconnect_db", { connectionId, clientAttempt });
|
||||
}
|
||||
|
||||
export async function checkConnectionHealth(connectionId: string): Promise<void> {
|
||||
return invoke("check_connection_health", { connectionId });
|
||||
return invokeBackend("check_connection_health", { connectionId });
|
||||
}
|
||||
|
||||
export async function connectionIdentifierQuote(connectionId: string, database?: string): Promise<string | undefined> {
|
||||
@@ -3405,7 +3414,7 @@ export async function startTransfer(request: TransferRequest, onProgress: (progr
|
||||
await invoke("start_transfer", { request });
|
||||
} catch (e) {
|
||||
unlisten?.();
|
||||
reject(e);
|
||||
reject(e instanceof BackendErrorException ? e : new BackendErrorException(e));
|
||||
}
|
||||
})();
|
||||
});
|
||||
@@ -3561,7 +3570,7 @@ export async function importTableFile(request: TableImportRequest, onProgress: (
|
||||
return summary;
|
||||
} catch (e) {
|
||||
unlisten();
|
||||
throw e;
|
||||
throw e instanceof BackendErrorException ? e : new BackendErrorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3707,7 +3716,7 @@ export async function startTableExport(request: TableExportRequest, onProgress:
|
||||
onProgress(event.payload);
|
||||
if (event.payload.status === "Done" || event.payload.status === "Error" || event.payload.status === "Cancelled") {
|
||||
if (event.payload.status === "Error") {
|
||||
finish(() => rejectTerminal(new Error(event.payload.errorMessage || "Export failed")));
|
||||
finish(() => rejectTerminal(new BackendErrorException(event.payload.errorMessage || "Export failed")));
|
||||
} else {
|
||||
finish(() => resolveTerminal(event.payload));
|
||||
}
|
||||
@@ -3720,7 +3729,7 @@ export async function startTableExport(request: TableExportRequest, onProgress:
|
||||
settled = true;
|
||||
unlisten?.();
|
||||
}
|
||||
throw error;
|
||||
throw error instanceof BackendErrorException ? error : new BackendErrorException(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3752,7 +3761,7 @@ export async function startQueryResultExport(request: QueryResultExportRequest,
|
||||
onProgress(event.payload);
|
||||
if (event.payload.status === "Done" || event.payload.status === "Error" || event.payload.status === "Cancelled") {
|
||||
if (event.payload.status === "Error") {
|
||||
finish(() => rejectTerminal(new Error(event.payload.errorMessage || "Export failed")));
|
||||
finish(() => rejectTerminal(new BackendErrorException(event.payload.errorMessage || "Export failed")));
|
||||
} else {
|
||||
finish(() => resolveTerminal(event.payload));
|
||||
}
|
||||
@@ -3765,7 +3774,7 @@ export async function startQueryResultExport(request: QueryResultExportRequest,
|
||||
settled = true;
|
||||
unlisten?.();
|
||||
}
|
||||
throw error;
|
||||
throw error instanceof BackendErrorException ? error : new BackendErrorException(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user