Skip to content

Commit e654962

Browse files
committed
fix: turn atlas-connect-cluster async
1 parent 5b7ba55 commit e654962

File tree

1 file changed

+94
-5
lines changed

1 file changed

+94
-5
lines changed

src/tools/atlas/metadata/connectCluster.ts

Lines changed: 94 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,36 @@ const EXPIRY_MS = 1000 * 60 * 60 * 12; // 12 hours
1111
function sleep(ms: number): Promise<void> {
1212
return new Promise((resolve) => setTimeout(resolve, ms));
1313
}
14+
1415
export class ConnectClusterTool extends AtlasToolBase {
1516
protected name = "atlas-connect-cluster";
16-
protected description = "Connect to MongoDB Atlas cluster";
17+
protected description = "Connect to/Query status of MongoDB Atlas cluster";
1718
protected operationType: OperationType = "metadata";
1819
protected argsShape = {
1920
projectId: z.string().describe("Atlas project ID"),
2021
clusterName: z.string().describe("Atlas cluster name"),
2122
};
2223

23-
protected async execute({ projectId, clusterName }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
24+
private async queryConnection(projectId: string, clusterName: string) : Promise<"connected" | "disconnected" | "connecting" | "connected-to-other-cluster"> {
25+
if (!this.session.connectedAtlasCluster) {
26+
return "disconnected";
27+
}
28+
29+
if (this.session.connectedAtlasCluster.projectId !== projectId || this.session.connectedAtlasCluster.clusterName !== clusterName) {
30+
return "connected-to-other-cluster";
31+
}
32+
33+
if (!this.session.serviceProvider) {
34+
return "connecting";
35+
}
36+
37+
await this.session.serviceProvider.runCommand("admin", {
38+
ping: 1,
39+
});
40+
return "connected";
41+
}
42+
43+
private async prepareClusterConnection(projectId: string, clusterName: string) : Promise<string> {
2444
await this.session.disconnect();
2545

2646
const cluster = await inspectCluster(this.session.apiClient, projectId, clusterName);
@@ -83,9 +103,13 @@ export class ConnectClusterTool extends AtlasToolBase {
83103
cn.searchParams.set("authSource", "admin");
84104
const connectionString = cn.toString();
85105

106+
return connectionString;
107+
}
108+
109+
private async connectToCluster(connectionString: string): Promise<void> {
86110
let lastError: Error | undefined = undefined;
87111

88-
for (let i = 0; i < 20; i++) {
112+
for (let i = 0; i < 600; i++) { // try for 5 minutes
89113
try {
90114
await this.session.connectToMongoDB(connectionString, this.config.connectOptions);
91115
lastError = undefined;
@@ -104,16 +128,81 @@ export class ConnectClusterTool extends AtlasToolBase {
104128
await sleep(500); // wait for 500ms before retrying
105129
}
106130
}
107-
131+
108132
if (lastError) {
133+
void this.session.apiClient.deleteDatabaseUser({
134+
params: {
135+
path: {
136+
groupId: this.session.connectedAtlasCluster?.projectId || "",
137+
username: this.session.connectedAtlasCluster?.username || "",
138+
databaseName: "admin",
139+
},
140+
},
141+
}).catch((err: unknown) => {
142+
const error = err instanceof Error ? err : new Error(String(err));
143+
logger.debug(
144+
LogId.atlasConnectFailure,
145+
"atlas-connect-cluster",
146+
`error deleting database user: ${error.message}`
147+
);
148+
});
149+
this.session.connectedAtlasCluster = undefined;
109150
throw lastError;
110151
}
152+
}
153+
154+
protected async execute({ projectId, clusterName }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
155+
try {
156+
const state = await this.queryConnection(projectId, clusterName);
157+
switch (state) {
158+
case "connected":
159+
return {
160+
content: [
161+
{
162+
type: "text",
163+
text: "Cluster is already connected.",
164+
},
165+
],
166+
};
167+
case "connecting":
168+
return {
169+
content: [
170+
{
171+
type: "text",
172+
text: "Cluster is connecting...",
173+
},
174+
],
175+
};
176+
}
177+
} catch (err: unknown) {
178+
const error = err instanceof Error ? err : new Error(String(err));
179+
logger.debug(
180+
LogId.atlasConnectFailure,
181+
"atlas-connect-cluster",
182+
`error querying cluster: ${error.message}`
183+
);
184+
// fall through to create new connection
185+
}
186+
187+
const connectionString = await this.prepareClusterConnection(projectId, clusterName);
188+
process.nextTick(async () => {
189+
try {
190+
await this.connectToCluster(connectionString);
191+
} catch (err: unknown) {
192+
const error = err instanceof Error ? err : new Error(String(err));
193+
logger.debug(
194+
LogId.atlasConnectFailure,
195+
"atlas-connect-cluster",
196+
`error connecting to cluster: ${error.message}`
197+
);
198+
}
199+
});
111200

112201
return {
113202
content: [
114203
{
115204
type: "text",
116-
text: `Connected to cluster "${clusterName}"`,
205+
text: `Connecting to cluster "${clusterName}"...`,
117206
},
118207
],
119208
};

0 commit comments

Comments
 (0)