fix(settings): correct connection and query timeout limits
This commit is contained in:
15 files changed
+180
-20
No files matched your search
@@ -33,6 +33,7 @@ import DatabaseIcon from "@/components/icons/DatabaseIcon.vue";
|
||||
import * as api from "@/lib/backend/api";
|
||||
import { isTauriRuntime } from "@/lib/backend/tauriRuntime";
|
||||
import { applyParsedConnectionUrl, normalizeMongoConnectionString, parseConnectionUrl } from "@/lib/connection/connectionUrl";
|
||||
import { MAX_CONNECT_TIMEOUT_SECS, MAX_QUERY_TIMEOUT_SECS } from "@/lib/connection/timeoutLimits";
|
||||
import { buildOracleTnsConnectionString, normalizeOracleTnsAdminPath, parseOracleTnsConnectionString } from "@/lib/connection/oracleTnsConnection";
|
||||
import { connectionDeepLinkServiceHydrationValue, parseConnectionDeepLink, parseServiceConnectionUrl, type ConnectionDeepLinkDraft } from "@/lib/connection/connectionDeepLink";
|
||||
import { connectionUrlPlaceholder as getUrlPlaceholder } from "@/lib/connection/connectionPresentation";
|
||||
@@ -3703,10 +3704,9 @@ function connectionConfigForSubmit(id: string, generatedName = ""): ConnectionCo
|
||||
// service, SID, and descriptor JDBC strings exactly as before.
|
||||
config.connection_string = undefined;
|
||||
}
|
||||
const connectTimeout = Number(config.connect_timeout_secs);
|
||||
config.connect_timeout_secs = config.connect_timeout_inherit === true ? normalizeGlobalConnectTimeoutSecs(editGlobalConnectTimeoutSecs.value) : Number.isFinite(connectTimeout) && connectTimeout > 0 ? connectTimeout : 10;
|
||||
config.connect_timeout_secs = config.connect_timeout_inherit === true ? normalizeGlobalConnectTimeoutSecs(editGlobalConnectTimeoutSecs.value) : normalizeGlobalConnectTimeoutSecs(config.connect_timeout_secs);
|
||||
const queryTimeout = Number(config.query_timeout_secs);
|
||||
config.query_timeout_secs = config.query_timeout_inherit === true ? normalizeGlobalQueryTimeoutSecs(editGlobalQueryTimeoutSecs.value) : Number.isFinite(queryTimeout) && queryTimeout >= 0 ? queryTimeout : 30;
|
||||
config.query_timeout_secs = config.query_timeout_inherit === true ? normalizeGlobalQueryTimeoutSecs(editGlobalQueryTimeoutSecs.value) : normalizeGlobalQueryTimeoutSecs(queryTimeout);
|
||||
const idleTimeout = Number(config.idle_timeout_secs);
|
||||
config.idle_timeout_secs = Number.isFinite(idleTimeout) && idleTimeout >= 0 ? idleTimeout : 60;
|
||||
const keepaliveInterval = Number(config.keepalive_interval_secs);
|
||||
@@ -5007,6 +5007,26 @@ function validateTransportLayers(config: LegacyConnectionConfig) {
|
||||
});
|
||||
}
|
||||
|
||||
function clampQueryTimeoutInput(event: Event, target: "global" | "connection") {
|
||||
const input = event.target as HTMLInputElement;
|
||||
if (input.value === "") return;
|
||||
const value = Number(input.value);
|
||||
if (!Number.isFinite(value) || value <= MAX_QUERY_TIMEOUT_SECS) return;
|
||||
input.value = String(MAX_QUERY_TIMEOUT_SECS);
|
||||
if (target === "global") editGlobalQueryTimeoutSecs.value = MAX_QUERY_TIMEOUT_SECS;
|
||||
else form.value.query_timeout_secs = MAX_QUERY_TIMEOUT_SECS;
|
||||
}
|
||||
|
||||
function clampConnectTimeoutInput(event: Event, target: "global" | "connection") {
|
||||
const input = event.target as HTMLInputElement;
|
||||
if (input.value === "") return;
|
||||
const value = Number(input.value);
|
||||
if (!Number.isFinite(value) || value <= MAX_CONNECT_TIMEOUT_SECS) return;
|
||||
input.value = String(MAX_CONNECT_TIMEOUT_SECS);
|
||||
if (target === "global") editGlobalConnectTimeoutSecs.value = MAX_CONNECT_TIMEOUT_SECS;
|
||||
else form.value.connect_timeout_secs = MAX_CONNECT_TIMEOUT_SECS;
|
||||
}
|
||||
|
||||
async function persistGlobalTimeoutDrafts() {
|
||||
const nextConnect = normalizeGlobalConnectTimeoutSecs(editGlobalConnectTimeoutSecs.value);
|
||||
const nextQuery = normalizeGlobalQueryTimeoutSecs(editGlobalQueryTimeoutSecs.value);
|
||||
@@ -7728,13 +7748,41 @@ function openExternalUrl(url: string) {
|
||||
<div class="col-span-3 grid grid-cols-2 gap-2">
|
||||
<div class="grid min-w-0 grid-cols-[auto_minmax(0,1fr)] items-center gap-x-2 gap-y-1 rounded border px-2 py-1.5 sm:flex" :class="form.connect_timeout_inherit === true ? 'border-primary/60 bg-background' : 'border-border bg-muted/30 text-muted-foreground'">
|
||||
<input id="connect-timeout-global" v-model="form.connect_timeout_inherit" type="radio" name="connect-timeout-scope" :value="true" class="h-3.5 w-3.5 shrink-0 accent-primary" />
|
||||
<label for="connect-timeout-global" class="min-w-0 flex-1 cursor-pointer truncate text-xs" :title="t('connection.useGlobalQueryTimeout')">{{ t("connection.useGlobalQueryTimeout") }}</label>
|
||||
<Input v-model.number="editGlobalConnectTimeoutSecs" type="number" min="1" max="300" step="1" class="col-span-2 h-7 w-full shrink-0 sm:col-span-1 sm:w-20" :disabled="form.connect_timeout_inherit !== true" />
|
||||
<div class="flex min-w-0 flex-1 items-center gap-1">
|
||||
<label for="connect-timeout-global" class="min-w-0 cursor-pointer truncate text-xs" :title="t('connection.useGlobalQueryTimeout')">{{ t("connection.useGlobalQueryTimeout") }}</label>
|
||||
<Tooltip>
|
||||
<TooltipTrigger as-child>
|
||||
<CircleHelp class="h-3.5 w-3.5 shrink-0 cursor-help text-muted-foreground hover:text-foreground" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top" align="center" class="max-w-[280px] text-xs leading-relaxed">
|
||||
{{ t("connection.globalConnectTimeoutHint") }}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Input
|
||||
v-model.number="editGlobalConnectTimeoutSecs"
|
||||
type="number"
|
||||
min="1"
|
||||
:max="MAX_CONNECT_TIMEOUT_SECS"
|
||||
step="1"
|
||||
class="col-span-2 h-7 w-full shrink-0 sm:col-span-1 sm:w-20"
|
||||
:disabled="form.connect_timeout_inherit !== true"
|
||||
@input="clampConnectTimeoutInput($event, 'global')"
|
||||
/>
|
||||
</div>
|
||||
<div class="grid min-w-0 grid-cols-[auto_minmax(0,1fr)] items-center gap-x-2 gap-y-1 rounded border px-2 py-1.5 sm:flex" :class="form.connect_timeout_inherit !== true ? 'border-primary/60 bg-background' : 'border-border bg-muted/30 text-muted-foreground'">
|
||||
<input id="connect-timeout-connection" v-model="form.connect_timeout_inherit" type="radio" name="connect-timeout-scope" :value="false" class="h-3.5 w-3.5 shrink-0 accent-primary" />
|
||||
<label for="connect-timeout-connection" class="min-w-0 flex-1 cursor-pointer truncate text-xs" :title="t('connection.useConnectionQueryTimeout')">{{ t("connection.useConnectionQueryTimeout") }}</label>
|
||||
<Input v-model.number="form.connect_timeout_secs" type="number" min="1" max="300" step="1" class="col-span-2 h-7 w-full shrink-0 sm:col-span-1 sm:w-20" :disabled="form.connect_timeout_inherit === true" />
|
||||
<Input
|
||||
v-model.number="form.connect_timeout_secs"
|
||||
type="number"
|
||||
min="1"
|
||||
:max="MAX_CONNECT_TIMEOUT_SECS"
|
||||
step="1"
|
||||
class="col-span-2 h-7 w-full shrink-0 sm:col-span-1 sm:w-20"
|
||||
:disabled="form.connect_timeout_inherit === true"
|
||||
@input="clampConnectTimeoutInput($event, 'connection')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -7750,13 +7798,23 @@ function openExternalUrl(url: string) {
|
||||
<div class="col-span-3 grid grid-cols-2 gap-2">
|
||||
<div class="grid min-w-0 grid-cols-[auto_minmax(0,1fr)] items-center gap-x-2 gap-y-1 rounded border px-2 py-1.5 sm:flex" :class="form.query_timeout_inherit === true ? 'border-primary/60 bg-background' : 'border-border bg-muted/30 text-muted-foreground'">
|
||||
<input id="query-timeout-global" v-model="form.query_timeout_inherit" type="radio" name="query-timeout-scope" :value="true" class="h-3.5 w-3.5 shrink-0 accent-primary" />
|
||||
<label for="query-timeout-global" class="min-w-0 flex-1 cursor-pointer truncate text-xs" :title="t('connection.useGlobalQueryTimeout')">{{ t("connection.useGlobalQueryTimeout") }}</label>
|
||||
<Input v-model.number="editGlobalQueryTimeoutSecs" type="number" min="0" max="300" step="1" class="col-span-2 h-7 w-full shrink-0 sm:col-span-1 sm:w-20" :disabled="form.query_timeout_inherit !== true" />
|
||||
<div class="flex min-w-0 flex-1 items-center gap-1">
|
||||
<label for="query-timeout-global" class="min-w-0 cursor-pointer truncate text-xs" :title="t('connection.useGlobalQueryTimeout')">{{ t("connection.useGlobalQueryTimeout") }}</label>
|
||||
<Tooltip>
|
||||
<TooltipTrigger as-child>
|
||||
<CircleHelp class="h-3.5 w-3.5 shrink-0 cursor-help text-muted-foreground hover:text-foreground" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top" align="center" class="max-w-[280px] text-xs leading-relaxed">
|
||||
{{ t("connection.globalQueryTimeoutHint") }}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Input v-model.number="editGlobalQueryTimeoutSecs" type="number" min="0" :max="MAX_QUERY_TIMEOUT_SECS" step="1" class="col-span-2 h-7 w-full shrink-0 sm:col-span-1 sm:w-20" :disabled="form.query_timeout_inherit !== true" @input="clampQueryTimeoutInput($event, 'global')" />
|
||||
</div>
|
||||
<div class="grid min-w-0 grid-cols-[auto_minmax(0,1fr)] items-center gap-x-2 gap-y-1 rounded border px-2 py-1.5 sm:flex" :class="form.query_timeout_inherit !== true ? 'border-primary/60 bg-background' : 'border-border bg-muted/30 text-muted-foreground'">
|
||||
<input id="query-timeout-connection" v-model="form.query_timeout_inherit" type="radio" name="query-timeout-scope" :value="false" class="h-3.5 w-3.5 shrink-0 accent-primary" />
|
||||
<label for="query-timeout-connection" class="min-w-0 flex-1 cursor-pointer truncate text-xs" :title="t('connection.useConnectionQueryTimeout')">{{ t("connection.useConnectionQueryTimeout") }}</label>
|
||||
<Input v-model.number="form.query_timeout_secs" type="number" min="0" max="300" step="1" class="col-span-2 h-7 w-full shrink-0 sm:col-span-1 sm:w-20" :disabled="form.query_timeout_inherit === true" />
|
||||
<Input v-model.number="form.query_timeout_secs" type="number" min="0" :max="MAX_QUERY_TIMEOUT_SECS" step="1" class="col-span-2 h-7 w-full shrink-0 sm:col-span-1 sm:w-20" :disabled="form.query_timeout_inherit === true" @input="clampQueryTimeoutInput($event, 'connection')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1012,6 +1012,8 @@ export default {
|
||||
connectTimeout: "Connection Timeout (seconds)",
|
||||
queryTimeout: "Query Timeout (seconds)",
|
||||
useGlobalQueryTimeout: "Global",
|
||||
globalConnectTimeoutHint: "Range: 1 to 300 seconds",
|
||||
globalQueryTimeoutHint: "Range: 0 to 3600 seconds. Set to 0 for no timeout.",
|
||||
useConnectionQueryTimeout: "Connection",
|
||||
idleTimeout: "Idle Timeout (seconds)",
|
||||
keepaliveInterval: "Keepalive Interval (seconds)",
|
||||
|
||||
@@ -795,6 +795,8 @@ export default withEnglishFallback({
|
||||
connectTimeout: "Tiempo de espera de conexión (segundos)",
|
||||
queryTimeout: "Tiempo de espera de consulta (segundos)",
|
||||
useGlobalQueryTimeout: "Global",
|
||||
globalConnectTimeoutHint: "Rango: de 1 a 300 segundos",
|
||||
globalQueryTimeoutHint: "Rango: de 0 a 3600 segundos. Establece 0 para no aplicar límite.",
|
||||
useConnectionQueryTimeout: "Conexión",
|
||||
idleTimeout: "Tiempo de espera inactivo (segundos)",
|
||||
keepaliveInterval: "Intervalo de keepalive (segundos)",
|
||||
|
||||
@@ -793,6 +793,8 @@ export default withEnglishFallback({
|
||||
connectTimeout: "Timeout Connessione (secondi)",
|
||||
queryTimeout: "Timeout Query (secondi)",
|
||||
useGlobalQueryTimeout: "Globale",
|
||||
globalConnectTimeoutHint: "Intervallo: da 1 a 300 secondi",
|
||||
globalQueryTimeoutHint: "Intervallo: da 0 a 3600 secondi. Imposta 0 per nessun limite.",
|
||||
useConnectionQueryTimeout: "Connessione",
|
||||
idleTimeout: "Timeout Inattività (secondi)",
|
||||
keepaliveInterval: "Intervallo keepalive (secondi)",
|
||||
|
||||
@@ -759,6 +759,8 @@ export default withEnglishFallback({
|
||||
connectTimeout: "接続タイムアウト(秒)",
|
||||
queryTimeout: "クエリタイムアウト(秒)",
|
||||
useGlobalQueryTimeout: "グローバル",
|
||||
globalConnectTimeoutHint: "1~300 秒の範囲で指定します",
|
||||
globalQueryTimeoutHint: "0~3600 秒の範囲で指定します。0 はタイムアウトなしです。",
|
||||
useConnectionQueryTimeout: "接続",
|
||||
idleTimeout: "アイドルタイムアウト(秒)",
|
||||
keepaliveInterval: "Keepalive 間隔(秒)",
|
||||
|
||||
@@ -909,6 +909,8 @@ export default withEnglishFallback({
|
||||
connectTimeout: "연결 제한 시간 (초)",
|
||||
queryTimeout: "쿼리 제한 시간 (초)",
|
||||
useGlobalQueryTimeout: "전역",
|
||||
globalConnectTimeoutHint: "범위: 1~300초",
|
||||
globalQueryTimeoutHint: "범위: 0~3600초. 0으로 설정하면 제한이 없습니다.",
|
||||
useConnectionQueryTimeout: "연결",
|
||||
idleTimeout: "유휴 제한 시간 (초)",
|
||||
keepaliveInterval: "킵얼라이브 간격 (초)",
|
||||
|
||||
@@ -794,6 +794,8 @@ export default withEnglishFallback({
|
||||
connectTimeout: "Timeout de Conexão (segundos)",
|
||||
queryTimeout: "Timeout de Consulta (segundos)",
|
||||
useGlobalQueryTimeout: "Global",
|
||||
globalConnectTimeoutHint: "Intervalo: de 1 a 300 segundos",
|
||||
globalQueryTimeoutHint: "Intervalo: de 0 a 3600 segundos. Defina 0 para não haver limite.",
|
||||
useConnectionQueryTimeout: "Conexão",
|
||||
idleTimeout: "Timeout de Inatividade (segundos)",
|
||||
keepaliveInterval: "Intervalo de keepalive (segundos)",
|
||||
|
||||
@@ -940,6 +940,8 @@ export default withEnglishFallback({
|
||||
connectTimeout: "连接超时(秒)",
|
||||
queryTimeout: "查询超时(秒)",
|
||||
useGlobalQueryTimeout: "全局",
|
||||
globalConnectTimeoutHint: "范围为1~300秒",
|
||||
globalQueryTimeoutHint: "范围为0~3600秒。设为0表示不限制",
|
||||
useConnectionQueryTimeout: "当前连接",
|
||||
idleTimeout: "空闲超时(秒)",
|
||||
keepaliveInterval: "保持连接间隔(秒)",
|
||||
|
||||
@@ -793,6 +793,8 @@ export default withEnglishFallback({
|
||||
connectTimeout: "連線逾時(秒)",
|
||||
queryTimeout: "查詢逾時(秒)",
|
||||
useGlobalQueryTimeout: "全域",
|
||||
globalConnectTimeoutHint: "範圍為1~300秒",
|
||||
globalQueryTimeoutHint: "範圍為0~3600秒。設為0表示不限制",
|
||||
useConnectionQueryTimeout: "目前連線",
|
||||
idleTimeout: "閒置逾時(秒)",
|
||||
keepaliveInterval: "保持連線間隔(秒)",
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { DEFAULT_CONNECT_TIMEOUT_SECS, normalizeConnectTimeoutSecs } from "@/lib/connection/timeoutLimits";
|
||||
|
||||
const dialogSource = readFileSync(new URL("../../../components/connection/ConnectionDialog.vue", import.meta.url), "utf8");
|
||||
|
||||
describe("ConnectionDialog timeout controls", () => {
|
||||
it("uses separate maximums for connection and query timeouts", () => {
|
||||
expect(dialogSource).toMatch(/v-model\.number="editGlobalConnectTimeoutSecs"[\s\S]*?min="1"[\s\S]*?:max="MAX_CONNECT_TIMEOUT_SECS"[\s\S]*?step="1"/);
|
||||
expect(dialogSource).toMatch(/v-model\.number="form\.connect_timeout_secs"[\s\S]*?min="1"[\s\S]*?:max="MAX_CONNECT_TIMEOUT_SECS"[\s\S]*?step="1"/);
|
||||
expect(dialogSource).toMatch(/v-model\.number="editGlobalQueryTimeoutSecs"[\s\S]*?min="0"[\s\S]*?:max="MAX_QUERY_TIMEOUT_SECS"[\s\S]*?step="1"/);
|
||||
expect(dialogSource).toMatch(/v-model\.number="form\.query_timeout_secs"[\s\S]*?min="0"[\s\S]*?:max="MAX_QUERY_TIMEOUT_SECS"[\s\S]*?step="1"/);
|
||||
expect(dialogSource).not.toContain('v-model.number="editGlobalQueryTimeoutSecs" type="number" min="0" max="300"');
|
||||
expect(dialogSource).not.toContain('v-model.number="form.query_timeout_secs" type="number" min="0" max="300"');
|
||||
expect(dialogSource).toContain("@input=\"clampQueryTimeoutInput($event, 'global')\"");
|
||||
expect(dialogSource).toContain("@input=\"clampQueryTimeoutInput($event, 'connection')\"");
|
||||
expect(dialogSource).toContain("@input=\"clampConnectTimeoutInput($event, 'global')\"");
|
||||
expect(dialogSource).toContain("@input=\"clampConnectTimeoutInput($event, 'connection')\"");
|
||||
expect(dialogSource).not.toContain('@blur="editGlobalQueryTimeoutSecs = normalizeGlobalQueryTimeoutSecs');
|
||||
});
|
||||
|
||||
it("restores the default when the connection timeout input is cleared", () => {
|
||||
expect(normalizeConnectTimeoutSecs("")).toBe(DEFAULT_CONNECT_TIMEOUT_SECS);
|
||||
expect(dialogSource).toContain("normalizeGlobalConnectTimeoutSecs(config.connect_timeout_secs)");
|
||||
expect(dialogSource).not.toContain("Number(config.connect_timeout_secs)");
|
||||
});
|
||||
|
||||
it("shows range help beside both global timeout labels", () => {
|
||||
expect(dialogSource).toContain('t("connection.globalConnectTimeoutHint")');
|
||||
expect(dialogSource).toContain('t("connection.globalQueryTimeoutHint")');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { loadTimeoutInheritanceBackup, TIMEOUT_INHERITANCE_BACKUP_STORAGE_KEY } from "@/lib/connection/timeoutInheritanceBackup";
|
||||
|
||||
const storage = new Map<string, string>();
|
||||
Object.defineProperty(globalThis, "localStorage", {
|
||||
configurable: true,
|
||||
value: {
|
||||
clear: () => storage.clear(),
|
||||
getItem: (key: string) => storage.get(key) ?? null,
|
||||
setItem: (key: string, value: string) => storage.set(key, value),
|
||||
},
|
||||
});
|
||||
|
||||
describe("timeout inheritance backup", () => {
|
||||
beforeEach(() => localStorage.clear());
|
||||
|
||||
it("preserves query timeouts up to one hour while connection timeouts remain capped at 300 seconds", () => {
|
||||
localStorage.setItem(
|
||||
TIMEOUT_INHERITANCE_BACKUP_STORAGE_KEY,
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
globalConnectTimeoutSecs: 301,
|
||||
globalQueryTimeoutSecs: 3600,
|
||||
connectSnapshots: { connection: 301 },
|
||||
querySnapshots: { longQuery: 3600, tooLong: 3601, unlimited: 0 },
|
||||
}),
|
||||
);
|
||||
|
||||
expect(loadTimeoutInheritanceBackup()).toEqual({
|
||||
version: 1,
|
||||
globalConnectTimeoutSecs: 300,
|
||||
globalQueryTimeoutSecs: 3600,
|
||||
connectSnapshots: { connection: 300 },
|
||||
querySnapshots: { longQuery: 3600, tooLong: 3600, unlimited: 0 },
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1,5 @@
|
||||
import { normalizeConnectTimeoutSecs, normalizeQueryTimeoutSecs } from "@/lib/connection/timeoutLimits";
|
||||
|
||||
export const TIMEOUT_INHERITANCE_BACKUP_STORAGE_KEY = "dbx-timeout-inheritance-backup-v1";
|
||||
|
||||
export interface TimeoutInheritanceBackup {
|
||||
@@ -8,12 +10,12 @@ export interface TimeoutInheritanceBackup {
|
||||
querySnapshots: Record<string, number>;
|
||||
}
|
||||
|
||||
function normalizeSnapshots(value: unknown, min: number): Record<string, number> {
|
||||
function normalizeSnapshots(value: unknown, normalize: (timeout: unknown) => number): Record<string, number> {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
||||
const snapshots: Record<string, number> = {};
|
||||
for (const [id, timeout] of Object.entries(value)) {
|
||||
if (!id.trim() || typeof timeout !== "number" || !Number.isFinite(timeout)) continue;
|
||||
snapshots[id] = Math.min(300, Math.max(min, Math.round(timeout)));
|
||||
snapshots[id] = normalize(timeout);
|
||||
}
|
||||
return snapshots;
|
||||
}
|
||||
@@ -29,10 +31,10 @@ export function loadTimeoutInheritanceBackup(): TimeoutInheritanceBackup | null
|
||||
if (!Number.isFinite(globalConnectTimeoutSecs) || !Number.isFinite(globalQueryTimeoutSecs)) return null;
|
||||
return {
|
||||
version: 1,
|
||||
globalConnectTimeoutSecs: Math.min(300, Math.max(1, Math.round(globalConnectTimeoutSecs))),
|
||||
globalQueryTimeoutSecs: Math.min(300, Math.max(0, Math.round(globalQueryTimeoutSecs))),
|
||||
connectSnapshots: normalizeSnapshots(parsed.connectSnapshots, 1),
|
||||
querySnapshots: normalizeSnapshots(parsed.querySnapshots, 0),
|
||||
globalConnectTimeoutSecs: normalizeConnectTimeoutSecs(globalConnectTimeoutSecs),
|
||||
globalQueryTimeoutSecs: normalizeQueryTimeoutSecs(globalQueryTimeoutSecs),
|
||||
connectSnapshots: normalizeSnapshots(parsed.connectSnapshots, normalizeConnectTimeoutSecs),
|
||||
querySnapshots: normalizeSnapshots(parsed.querySnapshots, normalizeQueryTimeoutSecs),
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
export const DEFAULT_CONNECT_TIMEOUT_SECS = 10;
|
||||
export const MAX_CONNECT_TIMEOUT_SECS = 300;
|
||||
export const DEFAULT_QUERY_TIMEOUT_SECS = 30;
|
||||
export const MAX_QUERY_TIMEOUT_SECS = 3600;
|
||||
|
||||
export function normalizeConnectTimeoutSecs(value: unknown): number {
|
||||
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_CONNECT_TIMEOUT_SECS;
|
||||
return Math.min(MAX_CONNECT_TIMEOUT_SECS, Math.max(1, Math.round(value)));
|
||||
}
|
||||
|
||||
export function normalizeQueryTimeoutSecs(value: unknown): number {
|
||||
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_QUERY_TIMEOUT_SECS;
|
||||
return Math.min(MAX_QUERY_TIMEOUT_SECS, Math.max(0, Math.round(value)));
|
||||
}
|
||||
+3
-1
@@ -238,7 +238,9 @@ describe("normalizeEditorSettings", () => {
|
||||
expect(normalizeEditorSettings({}).globalQueryTimeoutSecs).toBe(30);
|
||||
expect(normalizeEditorSettings({ queryTimeoutSecs: 45 } as any).globalQueryTimeoutSecs).toBe(45);
|
||||
expect(normalizeEditorSettings({ globalQueryTimeoutSecs: -1 }).globalQueryTimeoutSecs).toBe(0);
|
||||
expect(normalizeEditorSettings({ globalQueryTimeoutSecs: 301 }).globalQueryTimeoutSecs).toBe(300);
|
||||
expect(normalizeEditorSettings({ globalQueryTimeoutSecs: 301 }).globalQueryTimeoutSecs).toBe(301);
|
||||
expect(normalizeEditorSettings({ globalQueryTimeoutSecs: 3600 }).globalQueryTimeoutSecs).toBe(3600);
|
||||
expect(normalizeEditorSettings({ globalQueryTimeoutSecs: 3601 }).globalQueryTimeoutSecs).toBe(3600);
|
||||
expect(normalizeEditorSettings({ connectTimeoutInheritConnectionIds: ["one", "one", " ", "two"] }).connectTimeoutInheritConnectionIds).toEqual(["one", "two"]);
|
||||
expect(normalizeEditorSettings({ queryTimeoutInheritConnectionIds: ["one", "one", " ", "two"] }).queryTimeoutInheritConnectionIds).toEqual(["one", "two"]);
|
||||
expect(normalizeEditorSettings({}).timeoutInheritanceMigrationVersion).toBe(0);
|
||||
|
||||
@@ -9,6 +9,7 @@ import { type ColumnFormatterConfig, type CustomColumnFormatterConfig, normalize
|
||||
import { type DataGridCopyPreference, type DataGridExtractorOptions, DEFAULT_DATA_GRID_EXTRACTOR_OPTIONS, normalizeDataGridCopyPreference, normalizeDataGridExtractorOptions } from "@/lib/dataGrid/dataGridCopyExtractor";
|
||||
import { normalizeResultPageSize } from "@/lib/dataGrid/paginationPageSize";
|
||||
import { DEFAULT_QUERY_RESULT_MAX_ROWS, normalizeQueryResultMaxRows } from "@/lib/dataGrid/queryResultRowLimit";
|
||||
import { normalizeConnectTimeoutSecs, normalizeQueryTimeoutSecs } from "@/lib/connection/timeoutLimits";
|
||||
import { normalizeShortcutSettings, type ShortcutSettings } from "@/lib/editor/shortcutRegistry";
|
||||
import type { SavedSqlOpenTargetMode } from "@/lib/savedSql/savedSqlExecutionTarget";
|
||||
import type { ConnectionListSortMode } from "@/lib/sidebar/connectionListSort";
|
||||
@@ -800,13 +801,11 @@ const MIN_UI_SCALE = 0.75;
|
||||
const MAX_UI_SCALE = 2;
|
||||
|
||||
export function normalizeGlobalQueryTimeoutSecs(value: unknown): number {
|
||||
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_EDITOR_SETTINGS.globalQueryTimeoutSecs;
|
||||
return Math.min(300, Math.max(0, Math.round(value)));
|
||||
return normalizeQueryTimeoutSecs(value);
|
||||
}
|
||||
|
||||
export function normalizeGlobalConnectTimeoutSecs(value: unknown): number {
|
||||
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_EDITOR_SETTINGS.globalConnectTimeoutSecs;
|
||||
return Math.min(300, Math.max(1, Math.round(value)));
|
||||
return normalizeConnectTimeoutSecs(value);
|
||||
}
|
||||
|
||||
function normalizeUiScale(value: unknown): number {
|
||||
|
||||
Reference in new issue
Block a user