fix(mongodb): install legacy driver during connection test

This commit is contained in:
zipg
2026-08-14 14:45:35 +08:00
committed by GitHub
parent b6c4a0be0f
commit 325127419f
8 changed files with 64 additions and 12 deletions
@@ -8249,7 +8249,7 @@ function openExternalUrl(url: string) {
<div v-if="agentInstallError" class="space-y-2">
<div class="text-sm font-medium text-destructive">{{ t("connection.driverInstall.fullError") }}</div>
<pre class="max-h-56 overflow-auto whitespace-pre-wrap break-words rounded-md border bg-muted/30 p-3 text-xs leading-5 text-destructive">{{ agentInstallError }}</pre>
<pre class="max-h-56 min-w-0 max-w-full overflow-x-hidden overflow-y-auto whitespace-pre-wrap break-all [overflow-wrap:anywhere] rounded-md border bg-muted/30 p-3 text-xs leading-5 text-destructive">{{ agentInstallError }}</pre>
</div>
</div>
@@ -8266,14 +8266,14 @@ function openExternalUrl(url: string) {
</Dialog>
<Dialog v-model:open="showConnectionErrorDialog">
<DialogContent class="sm:max-w-[560px]">
<DialogContent class="min-w-0 sm:max-w-[680px]">
<DialogHeader>
<DialogTitle>{{ t("connection.connectFailedTitle") }}</DialogTitle>
</DialogHeader>
<div class="space-y-2">
<div class="min-w-0 space-y-2">
<div class="text-sm text-muted-foreground">{{ t("connection.fullErrorMessage") }}</div>
<pre class="max-h-72 overflow-auto whitespace-pre-wrap break-words rounded-md border bg-muted/30 p-3 text-xs leading-5 text-destructive">{{ connectionErrorDetail }}</pre>
<pre class="max-h-72 min-w-0 max-w-full overflow-x-hidden overflow-y-auto whitespace-pre-wrap break-all [overflow-wrap:anywhere] rounded-md border bg-muted/30 p-3 text-xs leading-5 text-destructive">{{ connectionErrorDetail }}</pre>
</div>
<DialogFooter class="gap-2">
@@ -0,0 +1,21 @@
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
const dialogSource = readFileSync(new URL("../../../components/connection/ConnectionDialog.vue", import.meta.url), "utf8");
describe("connection dialog error layout", () => {
it("installs a required agent before running a connection test", () => {
const installIndex = dialogSource.indexOf("await ensureRequiredAgentDriverInstalled(config);");
const testIndex = dialogSource.indexOf("const result = await testConnectionWithTimeout(config, runId);");
expect(installIndex).toBeGreaterThan(-1);
expect(testIndex).toBeGreaterThan(installIndex);
});
it("wraps long connection and driver installation errors inside the dialog", () => {
const wrappingClasses = "min-w-0 max-w-full overflow-x-hidden overflow-y-auto whitespace-pre-wrap break-all [overflow-wrap:anywhere]";
expect(dialogSource.split(wrappingClasses)).toHaveLength(3);
expect(dialogSource).toContain('<DialogContent class="min-w-0 sm:max-w-[680px]">');
});
});
@@ -37,15 +37,22 @@ export function agentDriverInstallKey(dbType: DatabaseType | undefined, driverPr
return driverProfile && driverProfile !== dbType ? driverProfile : dbType;
}
function usesManagedAgentDriver(dbType: DatabaseType | undefined, driverProfile?: string): boolean {
if (supportsDriverManagement(dbType)) return true;
if (dbType !== "mongodb") return false;
const profile = driverProfile?.trim().toLowerCase();
return profile === "mongodb-legacy" || profile === "mongodb_legacy" || profile === "legacy";
}
export function showAgentDriverInstallHint(dbType: DatabaseType | undefined, drivers: readonly AgentDriverInstallState[], driverProfile?: string): boolean {
if (!supportsDriverManagement(dbType)) return false;
if (!usesManagedAgentDriver(dbType, driverProfile)) return false;
const driverKey = agentDriverInstallKey(dbType, driverProfile);
if (!driverKey) return false;
return drivers.find((driver) => driver.db_type === driverKey)?.installed !== true;
}
export function hasAgentDriverUpdate(dbType: DatabaseType | undefined, drivers: readonly AgentDriverInstallState[], driverProfile?: string): boolean {
if (!supportsDriverManagement(dbType)) return false;
if (!usesManagedAgentDriver(dbType, driverProfile)) return false;
const driverKey = agentDriverInstallKey(dbType, driverProfile);
return drivers.find((driver) => driver.db_type === driverKey)?.update_available === true;
}
@@ -75,7 +75,10 @@ export function mongodbAuthFailureHint(message: string): string {
const source = message.match(/source='([^']+)'/)?.[1];
if (!source || !message.includes("Exception authenticating MongoCredential")) return message;
return `${message}\n\nCurrent authentication database: ${source}. If this user was created in admin, set Authentication database to admin or add authSource=admin to URL params.`;
const verificationHint = `Current authentication database: ${source}. The server rejected these credentials. Verify the username and password, and confirm that the user was created in ${source}.`;
if (source.toLowerCase() === "admin") return `${message}\n\n${verificationHint}`;
return `${message}\n\n${verificationHint} If the user was created in admin, set Authentication database to admin or add authSource=admin to URL params.`;
}
function parseMongoUrlParams(urlParams: string | undefined): URLSearchParams {
+9 -3
View File
@@ -260,9 +260,13 @@ pub fn mongo_legacy_error_with_auth_hint(err: &str) -> String {
return err.to_string();
};
let source = &source[..source_end];
format!(
"{err}\n\nCurrent authentication database: {source}. If this user was created in admin, set Authentication database to admin or add authSource=admin to URL params."
)
let verification_hint = format!(
"Current authentication database: {source}. The server rejected these credentials. Verify the username and password, and confirm that the user was created in {source}."
);
if source.eq_ignore_ascii_case("admin") {
return format!("{err}\n\n{verification_hint}");
}
format!("{err}\n\n{verification_hint} If the user was created in admin, set Authentication database to admin or add authSource=admin to URL params.")
}
pub fn mongo_uses_legacy_driver(config: &ConnectionConfig) -> bool {
@@ -861,6 +865,8 @@ mod tests {
assert!(hinted.starts_with(err));
assert!(hinted.contains("Current authentication database: admin"));
assert!(hinted.contains("The server rejected these credentials"));
assert!(!hinted.contains("add authSource=admin"));
}
#[test]
+1 -1
View File
@@ -5591,7 +5591,7 @@ mod tests {
assert_eq!(
mongo_legacy_error_with_auth_hint(err),
"Agent RPC error: Exception authenticating MongoCredential{mechanism=SCRAM-SHA-1, userName='rwuser', source='gray_lite_twin_fat'}\n\nCurrent authentication database: gray_lite_twin_fat. If this user was created in admin, set Authentication database to admin or add authSource=admin to URL params."
"Agent RPC error: Exception authenticating MongoCredential{mechanism=SCRAM-SHA-1, userName='rwuser', source='gray_lite_twin_fat'}\n\nCurrent authentication database: gray_lite_twin_fat. The server rejected these credentials. Verify the username and password, and confirm that the user was created in gray_lite_twin_fat. If the user was created in admin, set Authentication database to admin or add authSource=admin to URL params."
);
}
+7
View File
@@ -18,6 +18,13 @@ test("shows the agent driver install hint for Access when missing", () => {
assert.equal(showAgentDriverInstallHint("access", [{ db_type: "access", installed: false }]), true);
});
test("installs the MongoDB Legacy agent before testing a legacy MongoDB connection", () => {
assert.equal(agentDriverInstallKey("mongodb", "mongodb-legacy"), "mongodb");
assert.equal(showAgentDriverInstallHint("mongodb", [{ db_type: "mongodb", installed: false }], "mongodb-legacy"), true);
assert.equal(showAgentDriverInstallHint("mongodb", [{ db_type: "mongodb", installed: true }], "mongodb-legacy"), false);
assert.equal(showAgentDriverInstallHint("mongodb", [{ db_type: "mongodb", installed: false }], "mongodb"), false);
});
test("does not show agent driver install hints for built-in database types", () => {
assert.equal(showAgentDriverInstallHint("mysql", [{ db_type: "informix", installed: false }]), false);
});
+9 -1
View File
@@ -56,10 +56,18 @@ test("adds a MongoDB authSource hint for legacy authentication failures", () =>
assert.equal(
mongodbAuthFailureHint(message),
"Agent RPC error: Exception authenticating MongoCredential{mechanism=SCRAM-SHA-1, userName='rwuser', source='gray_lite_twin_fat'}\n\nCurrent authentication database: gray_lite_twin_fat. If this user was created in admin, set Authentication database to admin or add authSource=admin to URL params.",
"Agent RPC error: Exception authenticating MongoCredential{mechanism=SCRAM-SHA-1, userName='rwuser', source='gray_lite_twin_fat'}\n\nCurrent authentication database: gray_lite_twin_fat. The server rejected these credentials. Verify the username and password, and confirm that the user was created in gray_lite_twin_fat. If the user was created in admin, set Authentication database to admin or add authSource=admin to URL params.",
);
});
test("does not suggest authSource=admin when MongoDB already authenticated against admin", () => {
const message = "Agent RPC error: Exception authenticating MongoCredential{mechanism=SCRAM-SHA-1, userName='rwuser', source='admin'}";
const hinted = mongodbAuthFailureHint(message);
assert.match(hinted, /The server rejected these credentials/);
assert.doesNotMatch(hinted, /add authSource=admin/);
});
test("adds a MongoDB URL encoding hint for reserved password characters", () => {
const message = "MongoDB connection failed: Kind: An invalid argument was provided: password must be URL encoded";