diff --git a/src/components/connection/ConnectionDialog.vue b/src/components/connection/ConnectionDialog.vue index dcad24c26d..10c1727b90 100644 --- a/src/components/connection/ConnectionDialog.vue +++ b/src/components/connection/ConnectionDialog.vue @@ -16,6 +16,7 @@ import DatabaseIcon from "@/components/icons/DatabaseIcon.vue"; import * as api from "@/lib/api"; import { isTauriRuntime } from "@/lib/tauriRuntime"; import { applyParsedConnectionUrl, parseConnectionUrl } from "@/lib/connectionUrl"; +import { connectionUrlPlaceholder as getUrlPlaceholder } from "@/lib/connectionPresentation"; import { ArrowLeft, ChevronRight, Copy, ExternalLink, FolderOpen, Grid3X3, Link2, List, Search } from "lucide-vue-next"; type DbOption = { value: string; label: string }; @@ -419,6 +420,8 @@ const filteredDbCategories = computed(() => { const hasDbPickerResults = computed(() => filteredDbCategories.value.some((category) => category.options.length > 0)); const selectedDbIcon = computed(() => iconTypeMap[selectedType.value] || selectedProfile().icon || selectedType.value); const isJdbcConnection = computed(() => form.value.db_type === "jdbc"); + +const connectionUrlPlaceholder = computed(() => getUrlPlaceholder(form.value.db_type)); const canUseSsh = computed(() => form.value.db_type !== "sqlite" && form.value.db_type !== "jdbc"); const canUseProxy = computed( () => form.value.db_type !== "sqlite" && form.value.db_type !== "duckdb" && form.value.db_type !== "jdbc", @@ -817,7 +820,7 @@ function openExternalUrl(url: string) { diff --git a/src/lib/connectionPresentation.ts b/src/lib/connectionPresentation.ts index e16db1168b..b578f2babc 100644 --- a/src/lib/connectionPresentation.ts +++ b/src/lib/connectionPresentation.ts @@ -1,4 +1,4 @@ -import type { ConnectionConfig } from "@/types/database"; +import type { ConnectionConfig, DatabaseType } from "@/types/database"; type ConnectionPresentationConfig = Pick< ConnectionConfig, @@ -24,6 +24,53 @@ export function connectionEndpointLabel(connection?: ConnectionPresentationConfi return connection.host || connection.database || ""; } +export function connectionUrlPlaceholder(dbType: DatabaseType): string { + switch (dbType) { + case "mysql": + case "doris": + case "starrocks": + return "mysql://user:password@host:port/database"; + + case "postgres": + case "gaussdb": + case "redshift": + return "postgresql://user:password@host:port/database"; + + case "redis": + return "redis://:password@host:port/0"; + + case "sqlite": + return "sqlite:///absolute/path/to/database.db"; + + case "duckdb": + return "duckdb:///absolute/path/to/database.duckdb"; + + case "mongodb": + return "mongodb://user:password@host:port/database"; + + case "clickhouse": + return "clickhouse://user:password@host:port/database"; + + case "sqlserver": + return "mssql://user:password@host:port/database"; + + case "oracle": + return "oracle://user:password@host:port/service_name"; + + case "elasticsearch": + return "http://user:password@host:port"; + + case "dameng": + return "dm://user:password@host:port"; + + case "jdbc": + return "jdbc:mysql://host:3306/database"; + + default: + return "postgresql://user:password@host:port/database"; + } +} + export function connectionOptionSubtitle(connection?: ConnectionPresentationConfig): string { return [connectionDriverLabel(connection), connectionEndpointLabel(connection)].filter(Boolean).join(" ยท "); } diff --git a/tests/connectionUrlPlaceholder.test.ts b/tests/connectionUrlPlaceholder.test.ts new file mode 100644 index 0000000000..61942f2fb5 --- /dev/null +++ b/tests/connectionUrlPlaceholder.test.ts @@ -0,0 +1,51 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { connectionUrlPlaceholder } from "../src/lib/connectionPresentation.ts"; + +const expected: Record = { + mysql: "mysql://user:password@host:port/database", + doris: "mysql://user:password@host:port/database", + starrocks: "mysql://user:password@host:port/database", + postgres: "postgresql://user:password@host:port/database", + gaussdb: "postgresql://user:password@host:port/database", + redshift: "postgresql://user:password@host:port/database", + redis: "redis://:password@host:port/0", + sqlite: "sqlite:///absolute/path/to/database.db", + duckdb: "duckdb:///absolute/path/to/database.duckdb", + mongodb: "mongodb://user:password@host:port/database", + clickhouse: "clickhouse://user:password@host:port/database", + sqlserver: "mssql://user:password@host:port/database", + oracle: "oracle://user:password@host:port/service_name", + elasticsearch: "http://user:password@host:port", + dameng: "dm://user:password@host:port", + jdbc: "jdbc:mysql://host:3306/database", +}; + +for (const [dbType, placeholder] of Object.entries(expected)) { + test(`returns correct placeholder for ${dbType}`, () => { + assert.equal(connectionUrlPlaceholder(dbType as any), placeholder); + }); +} + +test("falls back to postgresql placeholder for unknown type", () => { + assert.equal(connectionUrlPlaceholder("unknown" as any), "postgresql://user:password@host:port/database"); +}); + +test("mysql-family types share the same placeholder", () => { + const mysql = connectionUrlPlaceholder("mysql"); + assert.equal(connectionUrlPlaceholder("doris"), mysql); + assert.equal(connectionUrlPlaceholder("starrocks"), mysql); +}); + +test("postgres-family types share the same placeholder", () => { + const postgres = connectionUrlPlaceholder("postgres"); + assert.equal(connectionUrlPlaceholder("gaussdb"), postgres); + assert.equal(connectionUrlPlaceholder("redshift"), postgres); +}); + +test("each placeholder starts with the expected scheme", () => { + for (const [dbType, placeholder] of Object.entries(expected)) { + const scheme = placeholder.split("://")[0]; + assert.ok(scheme.length > 0, `${dbType} placeholder should have a scheme`); + } +});