Skip to content

Commit 2018d24

Browse files
Merge pull request #992 from gjsjohnmurray/pre-fix-900
Rework initial connecting to prevent overprompting (#990)
2 parents 3ec2dea + c7e5c38 commit 2018d24

File tree

5 files changed

+117
-161
lines changed

5 files changed

+117
-161
lines changed

src/api/index.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ export class AtelierAPI {
188188
serverName = "";
189189
}
190190

191+
const ns = namespace ? namespace.toUpperCase() : conn.ns ? (conn.ns as string).toUpperCase() : undefined;
191192
if (serverName !== "") {
192193
const {
193194
webServer: { scheme, host, port, pathPrefix = "" },
@@ -199,7 +200,7 @@ export class AtelierAPI {
199200
active: this.externalServer || conn.active,
200201
apiVersion: workspaceState.get(this.configName + ":apiVersion", DEFAULT_API_VERSION),
201202
https: scheme === "https",
202-
ns: namespace || conn.ns,
203+
ns,
203204
host,
204205
port,
205206
username,
@@ -216,7 +217,7 @@ export class AtelierAPI {
216217
}
217218
} else {
218219
this._config = conn;
219-
this._config.ns = namespace || conn.ns;
220+
this._config.ns = ns;
220221
this._config.serverName = "";
221222
}
222223
}
@@ -342,14 +343,12 @@ export class AtelierAPI {
342343
authRequestMap.delete(target);
343344
if (this.wsOrFile && !checkingConnection) {
344345
setTimeout(() => {
345-
checkConnection(true, typeof this.wsOrFile === "object" ? this.wsOrFile : undefined);
346-
}, 1000);
346+
checkConnection(password ? true : false, typeof this.wsOrFile === "object" ? this.wsOrFile : undefined);
347+
}, 500);
347348
}
348349
throw { statusCode: response.status, message: response.statusText };
349350
}
350351
await this.updateCookies(response.headers.raw()["set-cookie"] || []);
351-
panel.text = `${this.connInfo}`;
352-
panel.tooltip = `Connected${pathPrefix ? " to " + pathPrefix : ""} as ${username}`;
353352
if (method === "HEAD") {
354353
authRequestMap.delete(target);
355354
return this.cookies;

src/commands/serverActions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export async function serverActions(): Promise<void> {
7171
const file = currentFile();
7272
const classname = file && file.name.toLowerCase().endsWith(".cls") ? file.name.slice(0, -4) : "";
7373
const classnameEncoded = encodeURIComponent(classname);
74-
const connInfo = `${host}:${port}${pathPrefix}[${nsEncoded}]`;
74+
const connInfo = `${host}:${port}${pathPrefix}[${nsEncoded.toUpperCase()}]`;
7575
const serverUrl = `${https ? "https" : "http"}://${host}:${port}${pathPrefix}`;
7676
const portalPath = `/csp/sys/UtilHome.csp?$NAMESPACE=${nsEncoded}`;
7777
const classRef = `/csp/documatic/%25CSP.Documatic.cls?LIBRARY=${nsEncoded}${

src/extension.ts

Lines changed: 111 additions & 66 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,
@@ -212,7 +211,8 @@ export async function resolvePassword(serverSpec): Promise<void> {
212211
session = await vscode.authentication.getSession(AUTHENTICATION_PROVIDER, scopes, { createIfNone: true });
213212
}
214213
if (session) {
215-
serverSpec.username = session.scopes[1];
214+
// If original spec lacked username use the one obtained by the authprovider
215+
serverSpec.username = serverSpec.username || session.scopes[1];
216216
serverSpec.password = session.accessToken;
217217
}
218218
}
@@ -240,11 +240,11 @@ export async function checkConnection(clearCookies = false, uri?: vscode.Uri): P
240240
const { apiTarget, configName } = connectionTarget(uri);
241241
if (clearCookies) {
242242
/// 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);
243+
await workspaceState.update(configName + ":host", undefined);
244+
await workspaceState.update(configName + ":port", undefined);
245+
await workspaceState.update(configName + ":password", undefined);
246+
await workspaceState.update(configName + ":apiVersion", undefined);
247+
await workspaceState.update(configName + ":docker", undefined);
248248
_onDidChangeConnection.fire();
249249
}
250250
let api = new AtelierAPI(apiTarget, false);
@@ -303,75 +303,111 @@ export async function checkConnection(clearCookies = false, uri?: vscode.Uri): P
303303
api.clearCookies();
304304
}
305305

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

309309
if (!api.config.host || !api.config.port || !api.config.ns) {
310310
const message = "'host', 'port' and 'ns' must be specified.";
311311
outputChannel.appendError(message);
312312
panel.text = `${PANEL_LABEL} $(error)`;
313313
panel.tooltip = `ERROR - ${message}`;
314-
disableConnection(configName);
314+
if (!api.externalServer) {
315+
await setConnectionState(configName, false);
316+
}
315317
return;
316318
}
317319
checkingConnection = true;
320+
321+
// What we do when api.serverInfo call succeeds
322+
const gotServerInfo = async (info) => {
323+
panel.text = api.connInfo;
324+
panel.tooltip = `Connected${pathPrefix ? " to " + pathPrefix : ""} as ${username}`;
325+
const hasHS = info.result.content.features.find((el) => el.name === "HEALTHSHARE" && el.enabled) !== undefined;
326+
reporter &&
327+
reporter.sendTelemetryEvent("connected", {
328+
serverVersion: info.result.content.version,
329+
healthshare: hasHS ? "yes" : "no",
330+
});
331+
332+
// Update CSP web app cache if required
333+
const key = (
334+
api.config.serverName && api.config.serverName != ""
335+
? `${api.config.serverName}:${api.config.ns}`
336+
: `${api.config.host}:${api.config.port}${api.config.pathPrefix}:${api.config.ns}`
337+
).toLowerCase();
338+
if (!cspApps.has(key)) {
339+
cspApps.set(key, await api.getCSPApps().then((data) => data.result.content || []));
340+
}
341+
if (!api.externalServer) {
342+
await setConnectionState(configName, true);
343+
}
344+
return;
345+
};
346+
347+
// Do the check
318348
return api
319349
.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) => {
350+
.then(gotServerInfo)
351+
.catch(async (error) => {
341352
let message = error.message;
342353
let errorMessage;
343354
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-
{
355+
let success = false;
356+
message = "Not Authorized.";
357+
errorMessage = `Authorization error: Check your credentials in Settings, and that you have sufficient privileges on the /api/atelier web application on ${connInfo}`;
358+
const username = api.config.username;
359+
if (username === "") {
360+
vscode.window.showErrorMessage(`Anonymous access rejected by ${connInfo}.`);
361+
if (!api.externalServer) {
362+
vscode.window.showErrorMessage("Connection has been disabled.");
363+
await setConnectionState(configName, false);
364+
}
365+
} else {
366+
success = await new Promise<boolean>((resolve) => {
367+
vscode.window
368+
.showInputBox({
355369
password: true,
356370
placeHolder: `Not Authorized. Enter password to connect as user '${username}' to ${connInfo}`,
357371
prompt: !api.externalServer ? "If no password is entered the connection will be disabled." : "",
358372
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);
373+
})
374+
.then(
375+
async (password) => {
376+
if (password) {
377+
await workspaceState.update(configName + ":password", password);
378+
resolve(
379+
api
380+
.serverInfo()
381+
.then(async (info): Promise<boolean> => {
382+
await gotServerInfo(info);
383+
_onDidChangeConnection.fire();
384+
return true;
385+
})
386+
.catch(async (error) => {
387+
console.log(`Second connect failed: ${error}`);
388+
await setConnectionState(configName, false);
389+
await workspaceState.update(configName + ":password", undefined);
390+
return false;
391+
})
392+
.finally(() => {
393+
checkingConnection = false;
394+
})
395+
);
396+
} else if (!api.externalServer) {
397+
await setConnectionState(configName, false);
398+
}
399+
console.log(`Finished prompting for password`);
400+
resolve(false);
401+
},
402+
(reason) => {
403+
console.log(`showInputBox for password dismissed: ${reason}`);
367404
}
368-
},
369-
connInfo
370-
);
405+
);
406+
});
407+
if (success) {
408+
return;
371409
}
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}`;
410+
}
375411
} else {
376412
errorMessage = `${message}\nCheck your server details in Settings (${connInfo}).`;
377413
}
@@ -395,16 +431,16 @@ export async function checkConnection(clearCookies = false, uri?: vscode.Uri): P
395431
});
396432
}
397433

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) {
434+
// Set objectscript.conn.active at WorkspaceFolder level if objectscript.conn is defined there,
435+
// else set it at Workspace level
436+
function setConnectionState(configName: string, active: boolean) {
401437
const connConfig: vscode.WorkspaceConfiguration = config("", configName);
402438
const target: vscode.ConfigurationTarget = connConfig.inspect("conn").workspaceFolderValue
403439
? vscode.ConfigurationTarget.WorkspaceFolder
404440
: vscode.ConfigurationTarget.Workspace;
405441
const targetConfig: any =
406442
connConfig.inspect("conn").workspaceFolderValue || connConfig.inspect("conn").workspaceValue;
407-
return connConfig.update("conn", { ...targetConfig, active: false }, target);
443+
return connConfig.update("conn", { ...targetConfig, active }, target);
408444
}
409445

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

523559
documentContentProvider = new DocumentContentProvider();
524-
xmlContentProvider = new XmlContentProvider();
525560
fileSystemProvider = new FileSystemProvider();
526561

527562
explorerProvider = new ObjectScriptExplorerProvider();
@@ -555,17 +590,27 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {
555590
vscode.workspace.workspaceFolders?.map((workspaceFolder) => {
556591
const uri = workspaceFolder.uri;
557592
const { configName } = connectionTarget(uri);
558-
toCheck.set(configName, uri);
593+
const serverName = uri.scheme === "file" ? config("conn", configName).server : configName;
594+
toCheck.set(serverName, uri);
559595
});
560596
for await (const oneToCheck of toCheck) {
561-
const configName = oneToCheck[0];
597+
const serverName = oneToCheck[0];
562598
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();
599+
try {
600+
try {
601+
await resolveConnectionSpec(serverName);
602+
} finally {
603+
await checkConnection(true, uri);
604+
}
605+
} catch (_) {
606+
// Ignore any failure
607+
continue;
608+
}
567609
}
568610

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

src/utils/index.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,6 @@ export function outputConsole(data: string[]): void {
3434
});
3535
}
3636

37-
import { InputBoxManager } from "./inputBoxManager";
38-
export { InputBoxManager };
39-
4037
// tslint:disable-next-line: interface-name
4138
export interface CurrentFile {
4239
name: string;

0 commit comments

Comments
 (0)