feat(connection): support Consul and Nacos service deep-link prefill

This commit is contained in:
二丫讲梵 authored and GitHub committed 2026-08-11 13:06:38 +08:00
1 parent 7c1d094946
commit 7852024690
13 files changed
+476 -26

No files matched your search

@@ -33,7 +33,7 @@ import * as api from "@/lib/backend/api";
import { isTauriRuntime } from "@/lib/backend/tauriRuntime";
import { applyParsedConnectionUrl, normalizeMongoConnectionString, parseConnectionUrl } from "@/lib/connection/connectionUrl";
import { buildOracleTnsConnectionString, normalizeOracleTnsAdminPath, parseOracleTnsConnectionString } from "@/lib/connection/oracleTnsConnection";
import { parseConnectionDeepLink, type ConnectionDeepLinkDraft } from "@/lib/connection/connectionDeepLink";
import { connectionDeepLinkServiceHydrationValue, parseConnectionDeepLink, parseServiceConnectionUrl, type ConnectionDeepLinkDraft } from "@/lib/connection/connectionDeepLink";
import { connectionUrlPlaceholder as getUrlPlaceholder } from "@/lib/connection/connectionPresentation";
import { parseGaussdbHosts, serializeGaussdbHosts, type GaussdbHostEntry } from "@/lib/connection/gaussdbHosts";
import { h2ConnectionModeForConfig, h2FileJdbcUrlWithPath, h2FilePathFromJdbcUrl, isH2SplitJdbcUrl, type H2ConnectionMode } from "@/lib/database/h2Connection";
@@ -3502,7 +3502,7 @@ function clearEditedConnectionErrorAfterSuccessfulTest() {
function applyConnectionUrlToForm(input: string): boolean {
try {
const draft = parseConnectionDeepLink(input);
const draft = parseConnectionDeepLink(input) ?? parseServiceConnectionUrl(input);
if (draft) {
applyConnectionDraftToForm({ ...draft, oneTime: undefined });
resetTestState();
@@ -4687,7 +4687,24 @@ function resetForm() {
const submittedOneTimePrefillKey = ref<string | null>(null);
function oneTimePrefillKey(draft: ConnectionDeepLinkDraft) {
return JSON.stringify([draft.name, draft.dbType, draft.driverProfile, draft.driverLabel, draft.host, draft.port, draft.portExplicit, draft.username, draft.password, draft.database, draft.urlParams, draft.ssl, draft.connectionString, draft.oracleConnectionType, draft.useMongoUrl]);
return JSON.stringify([
draft.name,
draft.dbType,
draft.driverProfile,
draft.driverLabel,
draft.host,
draft.port,
draft.portExplicit,
draft.username,
draft.password,
draft.database,
draft.urlParams,
draft.ssl,
draft.connectionString,
draft.oracleConnectionType,
draft.useMongoUrl,
draft.serviceConfig,
]);
}
function submitOneTimePrefill(draft: ConnectionDeepLinkDraft) {
@@ -4722,6 +4739,11 @@ function applyConnectionDraftToConfig(config: Omit<ConnectionConfig, "id">, draf
function applyConnectionDraftToForm(draft: ConnectionDeepLinkDraft) {
applyProfile(draft.driverProfile);
form.value = applyConnectionDraftToConfig(form.value, draft);
if (draft.serviceConfig?.kind === "consul") {
hydrateConsulFields(connectionDeepLinkServiceHydrationValue(draft.serviceConfig));
} else if (draft.serviceConfig?.kind === "nacos") {
hydrateNacosFields(connectionDeepLinkServiceHydrationValue(draft.serviceConfig));
}
oracleTnsAdminPath.value = parseOracleTnsConnectionString(form.value.connection_string)?.tnsAdmin || "";
selectedType.value = draft.driverProfile;
if (form.value.db_type === "h2") {
@@ -0,0 +1,164 @@
import { describe, expect, it } from "vitest";
import { connectionDeepLinkServiceHydrationValue, parseConnectionDeepLink, parseServiceConnectionUrl } from "@/lib/connection/connectionDeepLink";
import { connectionProfileForScheme } from "@/lib/connection/connectionUrl";
describe("service connection deep links", () => {
it("registers Consul and every Nacos spelling against canonical profiles", () => {
expect(connectionProfileForScheme("consul")).toMatchObject({ type: "consul", profile: "consul", defaultPort: 8500 });
for (const type of ["nacos-v2", "nacos-v3", "r-nacos", "nacos", "rnacos"]) {
expect(connectionProfileForScheme(type)).toMatchObject({ type: "nacos", profile: "nacos", defaultPort: 8848 });
}
});
it("builds a Consul endpoint and keeps the ACL token in the password field", () => {
const draft = parseConnectionDeepLink("dbx://connection/new?type=consul&host=consul.internal&password=acl-token");
expect(draft).toMatchObject({
dbType: "consul",
driverProfile: "consul",
host: "consul.internal",
port: 8500,
password: "acl-token",
serviceConfig: { kind: "consul", serverAddr: "http://consul.internal:8500" },
});
});
it("maps Nacos versions and aliases to explicit service profiles", () => {
const cases = [
["nacos", "v2"],
["nacos-v2", "v2"],
["nacos-v3", "v3"],
["rnacos", "rnacos"],
["r-nacos", "rnacos"],
] as const;
for (const [type, profile] of cases) {
const draft = parseConnectionDeepLink(`dbx://connection/new?type=${type}&host=127.0.0.1&user=nacos&password=secret`);
expect(draft?.dbType).toBe("nacos");
expect(draft?.driverProfile).toBe("nacos");
expect(draft?.serviceConfig).toMatchObject({
kind: "nacos",
profile,
serverAddr: "http://127.0.0.1:8848",
auth: { kind: "usernamePassword", username: "nacos", password: "secret" },
});
if (profile === "rnacos") expect(draft?.serviceConfig).toMatchObject({ rnacosHistoryEnabled: false });
}
});
it("preserves URL paths and lets top-level host, port and SSL fields override them", () => {
const link = new URL("dbx://connection/new");
link.searchParams.set("type", "nacos-v3");
link.searchParams.set("url", "https://nacos:secret@old.example:9443/proxy/nacos");
link.searchParams.set("host", "[2001:db8::8]");
link.searchParams.set("port", "18848");
link.searchParams.set("ssl", "false");
link.searchParams.set("user", "admin");
link.searchParams.set("password", "override");
const draft = parseConnectionDeepLink(link.toString());
expect(draft).toMatchObject({ host: "[2001:db8::8]", port: 18848, ssl: false, username: "admin", password: "override" });
expect(draft?.serviceConfig).toEqual({
kind: "nacos",
profile: "v3",
serverAddr: "http://[2001:db8::8]:18848/proxy/nacos",
auth: { kind: "usernamePassword", username: "admin", password: "override" },
});
});
it("accepts service-specific URL schemes and unwrapped IPv6 hosts", () => {
const link = new URL("dbx://connection/new");
link.searchParams.set("type", "nacos-v3");
link.searchParams.set("url", "nacos-v3://nacos:secret@old.example:8848/proxy/nacos");
link.searchParams.set("host", "2001:db8::9");
const draft = parseConnectionDeepLink(link.toString());
expect(draft?.serviceConfig).toMatchObject({
kind: "nacos",
profile: "v3",
serverAddr: "http://[2001:db8::9]:8848/proxy/nacos",
});
const inferred = new URL("dbx://connection/new");
inferred.searchParams.set("url", "consul://127.0.0.1:8500/proxy");
expect(parseConnectionDeepLink(inferred.toString())?.serviceConfig).toEqual({
kind: "consul",
serverAddr: "http://127.0.0.1:8500/proxy",
});
});
it("converts raw service URLs into drafts with specialized configuration", () => {
expect(parseServiceConnectionUrl("consul://remote.example:18500/prefix")).toMatchObject({
dbType: "consul",
host: "remote.example",
port: 18500,
serviceConfig: { kind: "consul", serverAddr: "http://remote.example:18500/prefix" },
});
expect(parseServiceConnectionUrl("nacos-v3://nacos:secret@remote.example:18848/nacos")).toMatchObject({
dbType: "nacos",
username: "nacos",
password: "secret",
serviceConfig: {
kind: "nacos",
profile: "v3",
serverAddr: "http://remote.example:18848/nacos",
auth: { kind: "usernamePassword", username: "nacos", password: "secret" },
},
});
expect(parseServiceConnectionUrl("mysql://root:secret@127.0.0.1:3306/app")).toBeNull();
});
it("accepts missing v and v=1, but rejects unsupported versions", () => {
expect(parseConnectionDeepLink("dbx://connection/new?type=consul&host=127.0.0.1")).not.toBeNull();
expect(parseConnectionDeepLink("dbx://connection/new?v=1&type=consul&host=127.0.0.1")).not.toBeNull();
expect(() => parseConnectionDeepLink("dbx://connection/new?v=2&type=consul&host=127.0.0.1")).toThrow(/Unsupported connection deep-link version/);
});
it("parses explicit false values and rejects invalid booleans", () => {
expect(parseConnectionDeepLink("dbx://connection/new?type=consul&ssl=false&one_time=false")).toMatchObject({
ssl: false,
oneTime: false,
serviceConfig: { kind: "consul", serverAddr: "http://127.0.0.1:8500" },
});
expect(() => parseConnectionDeepLink("dbx://connection/new?type=consul&ssl=maybe")).toThrow(/Invalid boolean value/);
expect(() => parseConnectionDeepLink("dbx://connection/new?type=consul&one_time=2")).toThrow(/Invalid boolean value/);
});
it("maps service drafts to the exact specialized form hydration values", () => {
expect(connectionDeepLinkServiceHydrationValue({ kind: "consul", serverAddr: "https://consul.example/prefix" })).toEqual({
serverAddr: "https://consul.example/prefix",
});
expect(
connectionDeepLinkServiceHydrationValue({
kind: "nacos",
profile: "v3",
serverAddr: "https://nacos.example/prefix",
auth: { kind: "usernamePassword", username: "nacos", password: "secret" },
}),
).toEqual({
implementation: "nacos",
versionMode: "v3",
serverAddr: "https://nacos.example/prefix",
auth: { kind: "usernamePassword", username: "nacos", password: "secret" },
});
expect(
connectionDeepLinkServiceHydrationValue({
kind: "nacos",
profile: "rnacos",
serverAddr: "http://rnacos.example/nacos",
auth: { kind: "none" },
rnacosHistoryEnabled: false,
}),
).toEqual({
implementation: "rnacos",
serverAddr: "http://rnacos.example/nacos",
rnacosHistoryEnabled: false,
auth: { kind: "none" },
});
});
it("rejects invalid ports, targets and incomplete Nacos credentials", () => {
expect(() => parseConnectionDeepLink("dbx://connection/new?type=consul&port=0")).toThrow(/Invalid connection port/);
expect(() => parseConnectionDeepLink("dbx://connection/new?type=consul&port=70000")).toThrow(/Invalid connection port/);
expect(() => parseConnectionDeepLink("dbx://connection/new?type=nacos-v2&password=secret")).toThrow(/username is required/);
expect(() => parseConnectionDeepLink("dbx://connection/new?type=consul&host=bad%2Fhost")).toThrow(/Invalid service connection host/);
expect(parseConnectionDeepLink("dbx://connection/edit?type=consul")).toBeNull();
});
});
@@ -0,0 +1,34 @@
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
const source = readFileSync(new URL("../../../components/connection/ConnectionDialog.vue", import.meta.url), "utf8");
describe("ConnectionDialog service deep-link hydration", () => {
it("hydrates specialized refs after generic fields and before one-time submit", () => {
const applyStart = source.indexOf("function applyConnectionDraftToForm");
const applyEnd = source.indexOf("function applyConnectionPrefill", applyStart);
const applyBody = source.slice(applyStart, applyEnd);
expect(applyBody.indexOf("applyConnectionDraftToConfig")).toBeGreaterThanOrEqual(0);
expect(applyBody.indexOf("hydrateConsulFields")).toBeGreaterThan(applyBody.indexOf("applyConnectionDraftToConfig"));
expect(applyBody.indexOf("hydrateNacosFields")).toBeGreaterThan(applyBody.indexOf("applyConnectionDraftToConfig"));
const prefillStart = source.indexOf("function applyConnectionPrefill");
const prefillEnd = source.indexOf("watch(\n open", prefillStart);
const prefillBody = source.slice(prefillStart, prefillEnd);
expect(prefillBody.indexOf("applyConnectionDraftToForm")).toBeLessThan(prefillBody.indexOf("submitOneTimePrefill"));
});
it("includes service configuration in one-time submission deduplication", () => {
const keyStart = source.indexOf("function oneTimePrefillKey");
const keyEnd = source.indexOf("function submitOneTimePrefill", keyStart);
expect(source.slice(keyStart, keyEnd)).toContain("draft.serviceConfig");
});
it("hydrates raw service URLs through the deep-link draft path", () => {
const applyStart = source.indexOf("function applyConnectionUrlToForm");
const applyEnd = source.indexOf("function hasPendingConnectionUrlInput", applyStart);
const applyBody = source.slice(applyStart, applyEnd);
expect(applyBody).toContain("parseConnectionDeepLink(input) ?? parseServiceConnectionUrl(input)");
expect(applyBody.indexOf("applyConnectionDraftToForm")).toBeLessThan(applyBody.indexOf("parseConnectionUrl(input"));
});
});
@@ -1,6 +1,16 @@
import type { DatabaseType } from "@/types/database";
import { connectionProfileForScheme, parseConnectionUrl } from "@/lib/connection/connectionUrl";
export type ConnectionDeepLinkServiceConfig =
| { kind: "consul"; serverAddr: string }
| {
kind: "nacos";
profile: "v2" | "v3" | "rnacos";
serverAddr: string;
auth: { kind: "none" } | { kind: "usernamePassword"; username: string; password: string };
rnacosHistoryEnabled?: false;
};
export interface ConnectionDeepLinkDraft {
name?: string;
dbType: DatabaseType;
@@ -18,6 +28,25 @@ export interface ConnectionDeepLinkDraft {
oracleConnectionType?: "service_name" | "sid";
useMongoUrl?: boolean;
oneTime?: boolean;
serviceConfig?: ConnectionDeepLinkServiceConfig;
}
export function connectionDeepLinkServiceHydrationValue(config: ConnectionDeepLinkServiceConfig): Record<string, unknown> {
if (config.kind === "consul") return { serverAddr: config.serverAddr };
if (config.profile === "rnacos") {
return {
implementation: "rnacos",
serverAddr: config.serverAddr,
rnacosHistoryEnabled: false,
auth: config.auth,
};
}
return {
implementation: "nacos",
versionMode: config.profile,
serverAddr: config.serverAddr,
auth: config.auth,
};
}
const CONNECTION_DEEP_LINK_TARGET = "connection/new";
@@ -34,16 +63,115 @@ function optionalParam(params: URLSearchParams, ...keys: string[]): string | und
return undefined;
}
function optionalNumberParam(params: URLSearchParams, ...keys: string[]): number | undefined {
function optionalPortParam(params: URLSearchParams, ...keys: string[]): number | undefined {
const value = optionalParam(params, ...keys);
if (!value) return undefined;
const numberValue = Number(value);
return Number.isFinite(numberValue) && numberValue > 0 ? numberValue : undefined;
if (!Number.isInteger(numberValue) || numberValue < 1 || numberValue > 65535) {
throw new Error(`Invalid connection port: ${value}`);
}
return numberValue;
}
function optionalBooleanParam(params: URLSearchParams, ...keys: string[]): boolean {
function optionalBooleanParam(params: URLSearchParams, ...keys: string[]): boolean | undefined {
const value = optionalParam(params, ...keys)?.toLowerCase();
return value === "true" || value === "1" || value === "yes" || value === "on";
if (value === undefined) return undefined;
if (["true", "1", "yes", "on"].includes(value)) return true;
if (["false", "0", "no", "off"].includes(value)) return false;
throw new Error(`Invalid boolean value: ${value}`);
}
type ServiceType = "consul" | "nacos-v2" | "nacos-v3" | "r-nacos";
function normalizeServiceType(value?: string): ServiceType | undefined {
switch (value?.trim().toLowerCase()) {
case "consul":
return "consul";
case "nacos":
case "nacos-v2":
return "nacos-v2";
case "nacos-v3":
return "nacos-v3";
case "rnacos":
case "r-nacos":
return "r-nacos";
default:
return undefined;
}
}
function validateProtocolVersion(params: URLSearchParams) {
const version = optionalParam(params, "v");
if (version !== undefined && version !== "1") {
throw new Error(`Unsupported connection deep-link version: ${version}`);
}
}
function validatedServiceHost(value: string): string {
const rawHost = value.trim();
if (!rawHost) throw new Error("Service connection host is required");
const unwrappedHost = rawHost.startsWith("[") && rawHost.endsWith("]") ? rawHost.slice(1, -1) : rawHost;
const authorityHost = unwrappedHost.includes(":") ? `[${unwrappedHost}]` : unwrappedHost;
let parsed: URL;
try {
parsed = new URL(`http://${authorityHost}:1`);
} catch {
throw new Error(`Invalid service connection host: ${value}`);
}
if (parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash || parsed.port !== "1") {
throw new Error(`Invalid service connection host: ${value}`);
}
return parsed.hostname;
}
function serviceEndpoint(rawConnectionUrl: string | undefined, serviceType: ServiceType, host: string | undefined, port: number, ssl: boolean): string {
let endpoint: URL;
if (rawConnectionUrl) {
try {
endpoint = new URL(rawConnectionUrl);
} catch {
throw new Error("Invalid service connection URL");
}
const sourceScheme = endpoint.protocol.replace(/:$/, "").toLowerCase();
const serviceScheme = normalizeServiceType(sourceScheme);
if (endpoint.protocol !== "http:" && endpoint.protocol !== "https:" && serviceScheme !== serviceType) {
throw new Error(`Unsupported service connection URL scheme: ${sourceScheme}`);
}
if (endpoint.search || endpoint.hash) throw new Error("Service connection URL must not contain query parameters or a fragment");
if (serviceScheme) {
const pathname = endpoint.pathname;
const hostWithPort = endpoint.host;
if (!hostWithPort) throw new Error("Service connection URL must include a host");
endpoint = new URL(`http://${hostWithPort}`);
endpoint.pathname = pathname;
}
} else {
const endpointHost = validatedServiceHost(host || "127.0.0.1");
endpoint = new URL(`${ssl ? "https" : "http"}://${endpointHost}:${port}`);
}
endpoint.username = "";
endpoint.password = "";
endpoint.protocol = ssl ? "https:" : "http:";
if (host) endpoint.hostname = validatedServiceHost(host);
endpoint.port = String(port);
return endpoint.toString().replace(/\/$/, "");
}
function serviceConfigForDraft(serviceType: ServiceType | undefined, rawConnectionUrl: string | undefined, host: string | undefined, port: number | undefined, ssl: boolean | undefined, username: string | undefined, password: string | undefined): ConnectionDeepLinkServiceConfig | undefined {
if (!serviceType) return undefined;
const defaultPort = serviceType === "consul" ? 8500 : 8848;
const serverAddr = serviceEndpoint(rawConnectionUrl, serviceType, host, port ?? defaultPort, ssl ?? false);
if (serviceType === "consul") return { kind: "consul", serverAddr };
if (password && !username) throw new Error("Nacos username is required when a password is provided");
const auth = username ? { kind: "usernamePassword" as const, username, password: password || "" } : { kind: "none" as const };
return {
kind: "nacos",
profile: serviceType === "nacos-v3" ? "v3" : serviceType === "r-nacos" ? "rnacos" : "v2",
serverAddr,
auth,
...(serviceType === "r-nacos" ? { rnacosHistoryEnabled: false as const } : {}),
};
}
function draftFromConnectionUrl(value: string, preferredProfile?: string): ConnectionDeepLinkDraft {
@@ -79,8 +207,17 @@ export function parseConnectionDeepLink(value: string): ConnectionDeepLinkDraft
if (normalizePath(url) !== CONNECTION_DEEP_LINK_TARGET) return null;
const params = url.searchParams;
const preferredProfile = optionalParam(params, "type");
validateProtocolVersion(params);
const preferredProfile = optionalParam(params, "type")?.toLowerCase();
const rawConnectionUrl = optionalParam(params, "url");
let serviceType = normalizeServiceType(preferredProfile);
if (!serviceType && rawConnectionUrl) {
try {
serviceType = normalizeServiceType(new URL(rawConnectionUrl).protocol.replace(/:$/, ""));
} catch {
// parseConnectionUrl below reports the canonical invalid URL error.
}
}
const draft: ConnectionDeepLinkDraft = rawConnectionUrl
? draftFromConnectionUrl(rawConnectionUrl, preferredProfile)
: (() => {
@@ -96,19 +233,44 @@ export function parseConnectionDeepLink(value: string): ConnectionDeepLinkDraft
})();
const oneTime = optionalBooleanParam(params, "one_time");
const explicitPort = optionalNumberParam(params, "port");
const explicitPort = optionalPortParam(params, "port");
const explicitSsl = optionalBooleanParam(params, "ssl");
const host = optionalParam(params, "host") ?? draft.host;
const port = explicitPort ?? draft.port;
const username = optionalParam(params, "user") ?? draft.username;
const password = optionalParam(params, "password") ?? draft.password;
const ssl = explicitSsl ?? draft.ssl;
const serviceConfig = serviceConfigForDraft(serviceType, rawConnectionUrl, host, port, ssl, username, password);
return {
...draft,
name: optionalParam(params, "name") ?? draft.name,
host: optionalParam(params, "host") ?? draft.host,
port: explicitPort ?? draft.port,
host,
port,
...((explicitPort !== undefined && draft.dbType === "sqlserver") || draft.portExplicit ? { portExplicit: true } : {}),
username: optionalParam(params, "user") ?? draft.username,
password: optionalParam(params, "password") ?? draft.password,
username,
password,
database: optionalParam(params, "database") ?? draft.database,
urlParams: optionalParam(params, "url_params") ?? draft.urlParams,
ssl: optionalBooleanParam(params, "ssl") ? true : draft.ssl,
...(oneTime ? { oneTime: true } : {}),
ssl,
...(oneTime !== undefined ? { oneTime } : {}),
...(serviceConfig ? { serviceConfig } : {}),
};
}
export function parseServiceConnectionUrl(value: string): ConnectionDeepLinkDraft | null {
let url: URL;
try {
url = new URL(value);
} catch {
return null;
}
const serviceType = normalizeServiceType(url.protocol.replace(/:$/, ""));
if (!serviceType) return null;
const deepLink = new URL("dbx://connection/new");
deepLink.searchParams.set("type", serviceType);
deepLink.searchParams.set("url", value);
return parseConnectionDeepLink(deepLink.toString());
}
@@ -38,6 +38,12 @@ const SCHEME_PROFILES: Record<string, ConnectionProfile> = {
redis: { type: "redis", profile: "redis", label: "Redis", defaultPort: 6379 },
rediss: { type: "redis", profile: "redis", label: "Redis", defaultPort: 6379 },
etcd: { type: "etcd", profile: "etcd", label: "etcd", defaultPort: 2379 },
consul: { type: "consul", profile: "consul", label: "Consul", defaultPort: 8500 },
"nacos-v2": { type: "nacos", profile: "nacos", label: "Nacos", defaultPort: 8848 },
"nacos-v3": { type: "nacos", profile: "nacos", label: "Nacos", defaultPort: 8848 },
"r-nacos": { type: "nacos", profile: "nacos", label: "r-nacos", defaultPort: 8848 },
nacos: { type: "nacos", profile: "nacos", label: "Nacos", defaultPort: 8848 },
rnacos: { type: "nacos", profile: "nacos", label: "r-nacos", defaultPort: 8848 },
zookeeper: { type: "zookeeper", profile: "zookeeper", label: "Apache ZooKeeper", defaultPort: 2181 },
mongodb: { type: "mongodb", profile: "mongodb", label: "MongoDB", defaultPort: 27017 },
"mongodb+srv": { type: "mongodb", profile: "mongodb", label: "MongoDB", defaultPort: 27017 },
@@ -81,6 +87,12 @@ const HTTP_SELECTED_PROFILES: Record<string, ConnectionProfile> = {
weaviate: SCHEME_PROFILES.weaviate,
chromadb: SCHEME_PROFILES.chromadb,
victoriametrics: SCHEME_PROFILES.victoriametrics,
consul: SCHEME_PROFILES.consul,
"nacos-v2": SCHEME_PROFILES["nacos-v2"],
"nacos-v3": SCHEME_PROFILES["nacos-v3"],
"r-nacos": SCHEME_PROFILES["r-nacos"],
nacos: SCHEME_PROFILES.nacos,
rnacos: SCHEME_PROFILES.rnacos,
};
function decodeUrlPart(value: string): string {
@@ -306,14 +318,16 @@ function isTidbCloudHost(host: string): boolean {
}
export function connectionProfileForScheme(scheme: string, preferredProfile?: string): ConnectionProfile | undefined {
if ((scheme === "http" || scheme === "https") && preferredProfile) {
return HTTP_SELECTED_PROFILES[preferredProfile];
const normalizedScheme = scheme.trim().toLowerCase();
const normalizedPreferredProfile = preferredProfile?.trim().toLowerCase();
if ((normalizedScheme === "http" || normalizedScheme === "https") && normalizedPreferredProfile) {
return HTTP_SELECTED_PROFILES[normalizedPreferredProfile];
}
// Cloudberry uses PostgreSQL URLs, so keep the selected product profile when parsing a pasted URL.
if ((scheme === "postgres" || scheme === "postgresql") && preferredProfile === "cloudberry") {
if ((normalizedScheme === "postgres" || normalizedScheme === "postgresql") && normalizedPreferredProfile === "cloudberry") {
return SCHEME_PROFILES.cloudberry;
}
return SCHEME_PROFILES[scheme];
return SCHEME_PROFILES[normalizedScheme];
}
function parseJdbcSqlServerUrl(source: string): ParsedConnectionUrl | null {