Skip to content

Commit 51917b9

Browse files
committed
Rework initial connecting to prevent overprompting (intersystems-community#900)
1 parent 9400a47 commit 51917b9

File tree

2 files changed

+111
-67
lines changed

2 files changed

+111
-67
lines changed

src/api/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,8 @@ export class AtelierAPI {
342342
authRequestMap.delete(target);
343343
if (this.wsOrFile && !checkingConnection) {
344344
setTimeout(() => {
345-
checkConnection(true, typeof this.wsOrFile === "object" ? this.wsOrFile : undefined);
346-
}, 1000);
345+
checkConnection(password ? true : false, typeof this.wsOrFile === "object" ? this.wsOrFile : undefined);
346+
}, 500);
347347
}
348348
throw { statusCode: response.status, message: response.statusText };
349349
}

src/extension.ts

Lines changed: 109 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ import {
7777
terminalWithDocker,
7878
notNull,
7979
currentFile,
80-
InputBoxManager,
8180
isImportableLocalFile,
8281
workspaceFolderOfUri,
8382
uriOfWorkspaceFolder,
@@ -240,11 +239,11 @@ export async function checkConnection(clearCookies = false, uri?: vscode.Uri): P
240239
const { apiTarget, configName } = connectionTarget(uri);
241240
if (clearCookies) {
242241
/// clean-up cached values
243-
workspaceState.update(configName + ":host", undefined);
244-
workspaceState.update(configName + ":port", undefined);
245-
workspaceState.update(configName + ":password", undefined);
246-
workspaceState.update(configName + ":apiVersion", undefined);
247-
workspaceState.update(configName + ":docker", undefined);
242+
await workspaceState.update(configName + ":host", undefined);
243+
await workspaceState.update(configName + ":port", undefined);
244+
await workspaceState.update(configName + ":password", undefined);
245+
await workspaceState.update(configName + ":apiVersion", undefined);
246+
await workspaceState.update(configName + ":docker", undefined);
248247
_onDidChangeConnection.fire();
249248
}
250249
let api = new AtelierAPI(apiTarget, false);
@@ -303,75 +302,111 @@ export async function checkConnection(clearCookies = false, uri?: vscode.Uri): P
303302
api.clearCookies();
304303
}
305304

306-
// Why must this be recreated here?
305+
// Why must this be recreated here? Maybe in case something has updated connection details since we last fetched them.
307306
api = new AtelierAPI(apiTarget, false);
308307

309308
if (!api.config.host || !api.config.port || !api.config.ns) {
310309
const message = "'host', 'port' and 'ns' must be specified.";
311310
outputChannel.appendError(message);
312311
panel.text = `${PANEL_LABEL} $(error)`;
313312
panel.tooltip = `ERROR - ${message}`;
314-
disableConnection(configName);
313+
if (!api.externalServer) {
314+
await setConnectionState(configName, false);
315+
}
315316
return;
316317
}
317318
checkingConnection = true;
319+
320+
// What we do when api.serverInfo call succeeds
321+
const gotServerInfo = async (info) => {
322+
panel.text = api.connInfo;
323+
panel.tooltip = `Connected${pathPrefix ? " to " + pathPrefix : ""} as ${username}`;
324+
const hasHS = info.result.content.features.find((el) => el.name === "HEALTHSHARE" && el.enabled) !== undefined;
325+
reporter &&
326+
reporter.sendTelemetryEvent("connected", {
327+
serverVersion: info.result.content.version,
328+
healthshare: hasHS ? "yes" : "no",
329+
});
330+
331+
// Update CSP web app cache if required
332+
const key = (
333+
api.config.serverName && api.config.serverName != ""
334+
? `${api.config.serverName}:${api.config.ns}`
335+
: `${api.config.host}:${api.config.port}${api.config.pathPrefix}:${api.config.ns}`
336+
).toLowerCase();
337+
if (!cspApps.has(key)) {
338+
cspApps.set(key, await api.getCSPApps().then((data) => data.result.content || []));
339+
}
340+
if (!api.externalServer) {
341+
await setConnectionState(configName, true);
342+
}
343+
return;
344+
};
345+
346+
// Do the check
318347
return api
319348
.serverInfo()
320-
.then(async (info) => {
321-
panel.text = api.connInfo;
322-
panel.tooltip = `Connected${pathPrefix ? " to " + pathPrefix : ""} as ${username}`;
323-
const hasHS = info.result.content.features.find((el) => el.name === "HEALTHSHARE" && el.enabled) !== undefined;
324-
reporter &&
325-
reporter.sendTelemetryEvent("connected", {
326-
serverVersion: info.result.content.version,
327-
healthshare: hasHS ? "yes" : "no",
328-
});
329-
// Update CSP web app cache if required
330-
const key = (
331-
api.config.serverName && api.config.serverName != ""
332-
? `${api.config.serverName}:${api.config.ns}`
333-
: `${api.config.host}:${api.config.port}${api.config.pathPrefix}:${api.config.ns}`
334-
).toLowerCase();
335-
if (!cspApps.has(key)) {
336-
cspApps.set(key, await api.getCSPApps().then((data) => data.result.content || []));
337-
}
338-
return;
339-
})
340-
.catch((error) => {
349+
.then(gotServerInfo)
350+
.catch(async (error) => {
341351
let message = error.message;
342352
let errorMessage;
343353
if (error.statusCode === 401) {
344-
setTimeout(() => {
345-
const username = api.config.username;
346-
if (username === "") {
347-
vscode.window.showErrorMessage(`Anonymous access rejected by ${connInfo}.`);
348-
if (!api.externalServer) {
349-
vscode.window.showErrorMessage("Connection has been disabled.");
350-
disableConnection(configName);
351-
}
352-
} else {
353-
InputBoxManager.showInputBox(
354-
{
354+
let success = false;
355+
message = "Not Authorized.";
356+
errorMessage = `Authorization error: Check your credentials in Settings, and that you have sufficient privileges on the /api/atelier web application on ${connInfo}`;
357+
const username = api.config.username;
358+
if (username === "") {
359+
vscode.window.showErrorMessage(`Anonymous access rejected by ${connInfo}.`);
360+
if (!api.externalServer) {
361+
vscode.window.showErrorMessage("Connection has been disabled.");
362+
await setConnectionState(configName, false);
363+
}
364+
} else {
365+
success = await new Promise<boolean>((resolve) => {
366+
vscode.window
367+
.showInputBox({
355368
password: true,
356369
placeHolder: `Not Authorized. Enter password to connect as user '${username}' to ${connInfo}`,
357370
prompt: !api.externalServer ? "If no password is entered the connection will be disabled." : "",
358371
ignoreFocusOut: true,
359-
},
360-
async (password) => {
361-
if (password) {
362-
workspaceState.update(configName + ":password", password);
363-
_onDidChangeConnection.fire();
364-
await checkConnection(false, uri);
365-
} else if (!api.externalServer) {
366-
disableConnection(configName);
372+
})
373+
.then(
374+
async (password) => {
375+
if (password) {
376+
await workspaceState.update(configName + ":password", password);
377+
resolve(
378+
api
379+
.serverInfo()
380+
.then(async (info): Promise<boolean> => {
381+
await gotServerInfo(info);
382+
_onDidChangeConnection.fire();
383+
return true;
384+
})
385+
.catch(async (error) => {
386+
console.log(`Second connect failed: ${error}`);
387+
await setConnectionState(configName, false);
388+
await workspaceState.update(configName + ":password", undefined);
389+
return false;
390+
})
391+
.finally(() => {
392+
checkingConnection = false;
393+
})
394+
);
395+
} else if (!api.externalServer) {
396+
await setConnectionState(configName, false);
397+
}
398+
console.log(`Finished prompting for password, got ${workspaceState.get(configName + ":password")}`);
399+
resolve(false);
400+
},
401+
(reason) => {
402+
console.log(`showInputBox for password dismissed: ${reason}`);
367403
}
368-
},
369-
connInfo
370-
);
404+
);
405+
});
406+
if (success) {
407+
return;
371408
}
372-
}, 1000);
373-
message = "Not Authorized.";
374-
errorMessage = `Authorization error: Check your credentials in Settings, and that you have sufficient privileges on the /api/atelier web application on ${connInfo}`;
409+
}
375410
} else {
376411
errorMessage = `${message}\nCheck your server details in Settings (${connInfo}).`;
377412
}
@@ -395,16 +430,16 @@ export async function checkConnection(clearCookies = false, uri?: vscode.Uri): P
395430
});
396431
}
397432

398-
// Set objectscript.conn.active = false at WorkspaceFolder level if objectscript.conn is defined there,
399-
// else set it false at Workspace level
400-
function disableConnection(configName: string) {
433+
// Set objectscript.conn.active at WorkspaceFolder level if objectscript.conn is defined there,
434+
// else set it at Workspace level
435+
function setConnectionState(configName: string, active: boolean) {
401436
const connConfig: vscode.WorkspaceConfiguration = config("", configName);
402437
const target: vscode.ConfigurationTarget = connConfig.inspect("conn").workspaceFolderValue
403438
? vscode.ConfigurationTarget.WorkspaceFolder
404439
: vscode.ConfigurationTarget.Workspace;
405440
const targetConfig: any =
406441
connConfig.inspect("conn").workspaceFolderValue || connConfig.inspect("conn").workspaceValue;
407-
return connConfig.update("conn", { ...targetConfig, active: false }, target);
442+
return connConfig.update("conn", { ...targetConfig, active }, target);
408443
}
409444

410445
// Promise to return the API of the servermanager
@@ -521,7 +556,6 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {
521556
serverManagerApi = await serverManager();
522557

523558
documentContentProvider = new DocumentContentProvider();
524-
xmlContentProvider = new XmlContentProvider();
525559
fileSystemProvider = new FileSystemProvider();
526560

527561
explorerProvider = new ObjectScriptExplorerProvider();
@@ -555,17 +589,27 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {
555589
vscode.workspace.workspaceFolders?.map((workspaceFolder) => {
556590
const uri = workspaceFolder.uri;
557591
const { configName } = connectionTarget(uri);
558-
toCheck.set(configName, uri);
592+
const serverName = uri.scheme === "file" ? config("conn", configName).server : configName;
593+
toCheck.set(serverName, uri);
559594
});
560595
for await (const oneToCheck of toCheck) {
561-
const configName = oneToCheck[0];
596+
const serverName = oneToCheck[0];
562597
const uri = oneToCheck[1];
563-
const serverName = uri.scheme === "file" ? config("conn", configName).server : configName;
564-
await resolveConnectionSpec(serverName);
565-
// Ignore any failure
566-
checkConnection(true, uri).finally();
598+
try {
599+
try {
600+
await resolveConnectionSpec(serverName);
601+
} finally {
602+
await checkConnection(true, uri);
603+
}
604+
} catch (_) {
605+
// Ignore any failure
606+
continue;
607+
}
567608
}
568609

610+
// This constructor instantiates an AtelierAPI object, so needs to happen after resolving and checking connections above
611+
xmlContentProvider = new XmlContentProvider();
612+
569613
const documentSelector = (...list) =>
570614
["file", ...schemas].reduce((acc, scheme) => acc.concat(list.map((language) => ({ scheme, language }))), []);
571615

0 commit comments

Comments
 (0)