diff --git a/crates/dbx-core/src/models/connection.rs b/crates/dbx-core/src/models/connection.rs index 185ed7ea22..7830182777 100644 --- a/crates/dbx-core/src/models/connection.rs +++ b/crates/dbx-core/src/models/connection.rs @@ -123,7 +123,8 @@ impl ConnectionConfig { if let Some(cs) = self.connection_string.as_deref().filter(|s| !s.is_empty()) { return cs.to_string(); } - format!("mongodb://{host}:{port}{db_part}") + let suffix = if params.is_empty() { String::new() } else { format!("?{params}") }; + format!("mongodb://{host}:{port}{db_part}{suffix}") } DatabaseType::Oracle => format!("oracle://{host}:{port}{db_part}"), DatabaseType::Elasticsearch => format!("http://{host}:{port}"), @@ -174,10 +175,11 @@ impl ConnectionConfig { if let Some(cs) = self.connection_string.as_deref().filter(|s| !s.is_empty()) { return cs.to_string(); } + let suffix = if params.is_empty() { String::new() } else { format!("?{params}") }; if self.username.is_empty() { - format!("mongodb://{host}:{port}{db_part}") + format!("mongodb://{host}:{port}{db_part}{suffix}") } else { - format!("mongodb://{username}:{password}@{host}:{port}{db_part}") + format!("mongodb://{username}:{password}@{host}:{port}{db_part}{suffix}") } } DatabaseType::Oracle => { @@ -234,7 +236,9 @@ impl ConnectionConfig { format!("ssl-mode=disabled&{}", filtered.join("&")) } } - DatabaseType::Postgres | DatabaseType::Redshift => value.trim_start_matches('?').to_string(), + DatabaseType::Postgres | DatabaseType::Redshift | DatabaseType::MongoDb => { + value.trim_start_matches('?').to_string() + } _ => value.trim_start_matches('?').to_string(), } } @@ -284,6 +288,13 @@ mod tests { } } + fn mongodb_config(username: &str, password: &str, database: Option<&str>) -> ConnectionConfig { + let mut config = mysql_config(username, password, database); + config.db_type = DatabaseType::MongoDb; + config.port = 17000; + config + } + #[test] fn mysql_url_encodes_oceanbase_username() { let config = mysql_config("user@tenant#cluster", "secret", None); @@ -324,6 +335,24 @@ mod tests { assert_eq!(config.connection_url(), "postgres://postgres:secret@10.1.2.3:2883/test?sslmode=disable"); } + #[test] + fn mongodb_form_url_without_params_does_not_force_topology_or_auth() { + let config = mongodb_config("root", "secret", Some("admin")); + + assert_eq!(config.connection_url(), "mongodb://root:secret@10.1.2.3:17000/admin"); + } + + #[test] + fn mongodb_form_url_appends_custom_params() { + let mut config = mongodb_config("root", "secret", Some("app")); + config.url_params = Some("?authSource=admin&authMechanism=SCRAM-SHA-1&directConnection=true".to_string()); + + assert_eq!( + config.connection_url(), + "mongodb://root:secret@10.1.2.3:17000/app?authSource=admin&authMechanism=SCRAM-SHA-1&directConnection=true" + ); + } + #[test] fn redacted_mysql_url_omits_credentials() { let config = mysql_config("user@tenant#cluster", "p@ss:word#1", Some("db/name")); @@ -360,4 +389,16 @@ mod tests { assert!(!url.contains("default")); assert!(!url.contains("redis-secret")); } + + #[test] + fn redacted_mongodb_url_keeps_custom_params_without_credentials() { + let mut config = mongodb_config("root", "secret", Some("admin")); + config.url_params = Some("authSource=admin&authMechanism=SCRAM-SHA-1".to_string()); + + let url = config.redacted_connection_url(); + + assert_eq!(url, "mongodb://10.1.2.3:17000/admin?authSource=admin&authMechanism=SCRAM-SHA-1"); + assert!(!url.contains("root")); + assert!(!url.contains("secret")); + } } diff --git a/src/components/connection/ConnectionDialog.vue b/src/components/connection/ConnectionDialog.vue index b8e3801b84..7e8a711aee 100644 --- a/src/components/connection/ConnectionDialog.vue +++ b/src/components/connection/ConnectionDialog.vue @@ -401,7 +401,7 @@ async function testConnection() { isTesting.value = true; testResult.value = null; try { - const config: ConnectionConfig = { ...form.value, id: editingId.value || uuid() }; + const config = connectionConfigForSubmit(editingId.value || uuid()); const msg = await api.testConnection(config); if (runId !== testRunId) return; testResult.value = { ok: true, message: msg }; @@ -415,6 +415,14 @@ async function testConnection() { } } +function connectionConfigForSubmit(id: string): ConnectionConfig { + const config: ConnectionConfig = { ...form.value, id }; + if (config.db_type === "mongodb" && !mongoUseUrl.value) { + config.connection_string = undefined; + } + return config; +} + function resetTestState() { testRunId += 1; isTesting.value = false; @@ -462,11 +470,11 @@ async function save() { resetTestState(); try { if (editingId.value) { - const updated: ConnectionConfig = { ...form.value, id: editingId.value }; + const updated = connectionConfigForSubmit(editingId.value); await store.updateConnection(updated); store.stopEditing(); } else { - const config: ConnectionConfig = { ...form.value, id: uuid() }; + const config = connectionConfigForSubmit(uuid()); await store.addConnection(config); open.value = false; await nextTick(); @@ -757,6 +765,20 @@ async function browseSshKeyPath() { :placeholder="t('connection.databasePlaceholder')" /> +
+ {{ t("connection.mongoLegacyHint") }} +
+