Skip to content

Commit e1052e6

Browse files
committed
fix #192 and also leverage servermanager API
1 parent 443c776 commit e1052e6

File tree

6 files changed

+213
-85
lines changed

6 files changed

+213
-85
lines changed

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,6 @@
246246
"group": "navigation@2",
247247
"when": "vscode-objectscript.connectActive && resourceScheme =~ /^isfs(-readonly)?$/"
248248
}
249-
250249
],
251250
"touchBar": [
252251
{

src/api/index.ts

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ export class AtelierAPI {
3939
// when FileSystemProvider used
4040
public externalServer = false;
4141

42+
// record of the constructor argument
43+
public readonly wsOrFile?: string | vscode.Uri;
44+
4245
public get ns(): string {
4346
return this.namespace || this._config.ns;
4447
}
@@ -81,9 +84,12 @@ export class AtelierAPI {
8184
return filename;
8285
}
8386

84-
public constructor(wsOrFile?: string | vscode.Uri) {
87+
public constructor(wsOrFile?: string | vscode.Uri, retryAfter401 = true) {
88+
if (retryAfter401) {
89+
this.wsOrFile = wsOrFile;
90+
}
8591
let workspaceFolderName = "";
86-
let namespace;
92+
let namespace = "";
8793
if (wsOrFile) {
8894
if (wsOrFile instanceof vscode.Uri) {
8995
if (wsOrFile.scheme === FILESYSTEM_SCHEMA || wsOrFile.scheme === FILESYSTEM_READONLY_SCHEMA) {
@@ -147,16 +153,15 @@ export class AtelierAPI {
147153
}
148154

149155
private setConnection(workspaceFolderName: string, namespace?: string): void {
150-
let serverName;
151156
this.configName = workspaceFolderName;
152-
if (config("intersystems.servers").has(workspaceFolderName.toLowerCase())) {
153-
this.externalServer = true;
154-
serverName = workspaceFolderName.toLowerCase();
155-
workspaceFolderName = currentWorkspaceFolder();
156-
}
157157
const conn = config("conn", workspaceFolderName);
158-
if (!serverName && conn.server) {
158+
let serverName = workspaceFolderName.toLowerCase();
159+
if (config("intersystems.servers").has(serverName)) {
160+
this.externalServer = true;
161+
} else if (conn.server) {
159162
serverName = conn.server;
163+
} else {
164+
serverName = "";
160165
}
161166

162167
if (serverName && serverName.length) {
@@ -176,6 +181,13 @@ export class AtelierAPI {
176181
password,
177182
pathPrefix,
178183
};
184+
185+
// Report server as inactive when no namespace has been determined,
186+
// otherwise output channel reports the issue.
187+
// This arises when a server-only workspace is editing the user's settings.json, or the .code-workspace file.
188+
if (this._config.ns === "" && this.externalServer) {
189+
this._config.active = false;
190+
}
179191
} else {
180192
this._config = conn;
181193
}
@@ -201,10 +213,6 @@ export class AtelierAPI {
201213
if (!active) {
202214
return Promise.reject();
203215
}
204-
if (!username || !username.length || !password || !password.length) {
205-
outputChannel.appendLine("username and password fields in settings are mandatory.");
206-
return Promise.reject();
207-
}
208216
if (minVersion > apiVersion) {
209217
return Promise.reject(`${path} not supported by API version ${apiVersion}`);
210218
}
@@ -318,6 +326,10 @@ export class AtelierAPI {
318326
workspaceState.update(this.configName + ":host", undefined);
319327
workspaceState.update(this.configName + ":port", undefined);
320328
setTimeout(checkConnection, 30000);
329+
} else if (error.statusCode === 401 && this.wsOrFile) {
330+
setTimeout(() => {
331+
checkConnection(true, typeof this.wsOrFile === "object" ? this.wsOrFile : undefined);
332+
}, 1000);
321333
}
322334
console.error(error);
323335
throw error;

src/commands/serverActions.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import * as vscode from "vscode";
22
import { config, workspaceState, checkConnection, FILESYSTEM_SCHEMA, FILESYSTEM_READONLY_SCHEMA } from "../extension";
3-
import { currentWorkspaceFolder, terminalWithDocker, currentFile } from "../utils";
3+
import { connectionTarget, terminalWithDocker, currentFile } from "../utils";
44
import { mainCommandMenu, mainSourceControlMenu } from "./studio";
55
import { AtelierAPI } from "../api";
66

77
export async function serverActions(): Promise<void> {
8-
const workspaceFolder = currentWorkspaceFolder();
9-
const api = new AtelierAPI(workspaceFolder);
8+
const { apiTarget, configName: workspaceFolder } = connectionTarget();
9+
const api = new AtelierAPI(apiTarget);
1010
const { active, host = "", ns = "", https, port = 0, username, password } = api.config;
1111
const { links } = config("conn");
1212
const nsEncoded = encodeURIComponent(ns);

src/extension.ts

Lines changed: 113 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,14 @@ import { ObjectScriptExplorerProvider } from "./explorer/explorer";
6262
import { WorkspaceNode } from "./explorer/models/workspaceNode";
6363
import { FileSystemProvider } from "./providers/FileSystemPovider/FileSystemProvider";
6464
import { WorkspaceSymbolProvider } from "./providers/WorkspaceSymbolProvider";
65-
import { currentWorkspaceFolder, outputChannel, portFromDockerCompose, terminalWithDocker, notNull } from "./utils";
65+
import {
66+
connectionTarget,
67+
currentWorkspaceFolder,
68+
outputChannel,
69+
portFromDockerCompose,
70+
terminalWithDocker,
71+
notNull,
72+
} from "./utils";
6673
import { ObjectScriptDiagnosticProvider } from "./providers/ObjectScriptDiagnosticProvider";
6774
import { DocumentRangeFormattingEditProvider } from "./providers/DocumentRangeFormattingEditProvider";
6875

@@ -96,7 +103,7 @@ export const config = (setting?: string, workspaceFolderName?: string): vscode.W
96103
) {
97104
workspaceFolderName = vscode.workspace.workspaceFolders[0].name;
98105
}
99-
let prefix;
106+
let prefix: string;
100107
const workspaceFolder = vscode.workspace.workspaceFolders.find(
101108
(el) => el.name.toLowerCase() === workspaceFolderName.toLowerCase()
102109
);
@@ -145,17 +152,19 @@ let reporter: TelemetryReporter = null;
145152

146153
let connectionSocket: WebSocket;
147154

148-
export const checkConnection = (clearCookies = false): void => {
149-
const workspaceFolder = currentWorkspaceFolder();
155+
let serverManagerApi: any;
156+
157+
export function checkConnection(clearCookies = false, uri?: vscode.Uri): void {
158+
const { apiTarget, configName } = connectionTarget(uri);
150159
if (clearCookies) {
151160
/// clean-up cached values
152-
workspaceState.update(workspaceFolder + ":host", undefined);
153-
workspaceState.update(workspaceFolder + ":port", undefined);
154-
workspaceState.update(workspaceFolder + ":password", undefined);
155-
workspaceState.update(workspaceFolder + ":apiVersion", undefined);
156-
workspaceState.update(workspaceFolder + ":docker", undefined);
161+
workspaceState.update(configName + ":host", undefined);
162+
workspaceState.update(configName + ":port", undefined);
163+
workspaceState.update(configName + ":password", undefined);
164+
workspaceState.update(configName + ":apiVersion", undefined);
165+
workspaceState.update(configName + ":docker", undefined);
157166
}
158-
let api = new AtelierAPI(workspaceFolder);
167+
let api = new AtelierAPI(apiTarget, false);
159168
const { active, host = "", port = 0, ns = "" } = api.config;
160169
let connInfo = `${host}:${port}[${ns}]`;
161170
if (!host.length || !port || !ns.length) {
@@ -168,9 +177,9 @@ export const checkConnection = (clearCookies = false): void => {
168177
panel.text = `${packageJson.displayName} - Disabled`;
169178
return;
170179
}
171-
if (!workspaceState.get(workspaceFolder + ":port") && !api.externalServer) {
180+
if (!workspaceState.get(configName + ":port") && !api.externalServer) {
172181
const { port: dockerPort, docker: withDocker } = portFromDockerCompose();
173-
workspaceState.update(workspaceFolder + ":docker", withDocker);
182+
workspaceState.update(configName + ":docker", withDocker);
174183
if (withDocker) {
175184
if (!dockerPort) {
176185
outputChannel.appendLine(
@@ -181,8 +190,8 @@ export const checkConnection = (clearCookies = false): void => {
181190
}
182191
terminalWithDocker();
183192
if (dockerPort !== port) {
184-
workspaceState.update(workspaceFolder + ":host", "localhost");
185-
workspaceState.update(workspaceFolder + ":port", dockerPort);
193+
workspaceState.update(configName + ":host", "localhost");
194+
workspaceState.update(configName + ":port", dockerPort);
186195
}
187196
connInfo = `localhost:${dockerPort}[${ns}]`;
188197
}
@@ -194,10 +203,15 @@ export const checkConnection = (clearCookies = false): void => {
194203
panel.text = `${connInfo} - Connected`;
195204
return;
196205
}
197-
api = new AtelierAPI(workspaceFolder);
206+
207+
// Why must this be recreated here?
208+
api = new AtelierAPI(apiTarget, false);
209+
198210
if (!api.config.host || !api.config.port || !api.config.ns) {
199-
outputChannel.appendLine("host, port and ns must be specified.");
211+
const message = "host, port and ns must be specified.";
212+
outputChannel.appendLine(message);
200213
panel.text = `${packageJson.displayName} - ERROR`;
214+
panel.tooltip = message;
201215
return;
202216
}
203217
api
@@ -222,32 +236,39 @@ export const checkConnection = (clearCookies = false): void => {
222236
.catch((error) => {
223237
let message = error.message;
224238
if (error instanceof StatusCodeError && error.statusCode === 401) {
225-
setTimeout(
226-
() =>
239+
setTimeout(() => {
240+
const username = api.config.username;
241+
if (username === "") {
242+
vscode.window.showErrorMessage(`Anonymous access rejected by ${connInfo}.`);
243+
if (!api.externalServer) {
244+
vscode.window.showErrorMessage("Connection has been disabled.");
245+
disableConnection(configName);
246+
}
247+
} else {
227248
vscode.window
228249
.showInputBox({
229250
password: true,
230-
placeHolder: "Not Authorized, please enter password to connect to: " + connInfo,
251+
placeHolder: `Not Authorized. Enter password to connect as user '${username}' to ${connInfo}`,
252+
prompt: !api.externalServer ? "If no password is entered the connection will be disabled." : "",
231253
ignoreFocusOut: true,
232254
})
233255
.then((password) => {
234256
if (password) {
235-
workspaceState.update(currentWorkspaceFolder() + ":password", password);
236-
checkConnection();
237-
} else {
238-
vscode.workspace.getConfiguration().update("objectscript.conn.active", false);
257+
workspaceState.update(configName + ":password", password);
258+
checkConnection(false, uri);
259+
} else if (!api.externalServer) {
260+
disableConnection(configName);
239261
}
240-
}),
241-
1000
242-
);
262+
});
263+
}
264+
}, 1000);
243265
message = "Not Authorized";
244266
outputChannel.appendLine(
245-
`Authorization error: please check your username/password in the settings,
246-
and if you have sufficient privileges on the server.`
267+
`Authorization error: Check your credentials in Settings, and that you have sufficient privileges on the /api/atelier web application on ${connInfo}`
247268
);
248269
} else {
249-
outputChannel.appendLine("Error: " + message);
250-
outputChannel.appendLine("Please check your network settings in the settings.");
270+
outputChannel.appendLine(`Error: ${message}`);
271+
outputChannel.appendLine(`Check your server details in Settings (${connInfo}).`);
251272
}
252273
console.error(error);
253274
panel.text = `${connInfo} - ERROR`;
@@ -257,44 +278,70 @@ export const checkConnection = (clearCookies = false): void => {
257278
.finally(() => {
258279
explorerProvider.refresh();
259280
});
260-
};
281+
}
282+
283+
// Set objectscript.conn.active = false at WorkspaceFolder level if objectscript.conn is defined there,
284+
// else set it false at Workspace level
285+
function disableConnection(configName: string) {
286+
const connConfig: vscode.WorkspaceConfiguration = config("", configName);
287+
const target: vscode.ConfigurationTarget = connConfig.inspect("conn").workspaceFolderValue
288+
? vscode.ConfigurationTarget.WorkspaceFolder
289+
: vscode.ConfigurationTarget.Workspace;
290+
const targetConfig: any =
291+
connConfig.inspect("conn").workspaceFolderValue || connConfig.inspect("conn").workspaceValue;
292+
return connConfig.update("conn", { ...targetConfig, active: false }, target);
293+
}
261294

262-
async function serverManager(): Promise<void> {
295+
// Promise to return the API of the servermanager
296+
async function serverManager(): Promise<any> {
263297
const extId = "intersystems-community.servermanager";
298+
let extension = vscode.extensions.getExtension(extId);
264299
const ignore =
265300
config("ignoreInstallServerManager") ||
266301
vscode.workspace.getConfiguration("intersystems.servers").get("/ignore", false);
267-
if (ignore || vscode.extensions.getExtension(extId)) {
268-
return;
302+
if (!extension) {
303+
if (ignore) {
304+
return;
305+
}
306+
await vscode.window
307+
.showInformationMessage(
308+
"The InterSystems® Server Manager extension is recommended to help you define connections and store passwords securely in your keychain.",
309+
"Install",
310+
"Skip",
311+
"Ignore"
312+
)
313+
.then(async (action) => {
314+
switch (action) {
315+
case "Install":
316+
await vscode.commands.executeCommand("workbench.extensions.search", `@tag:"intersystems"`);
317+
await vscode.commands.executeCommand("extension.open", extId);
318+
await vscode.commands.executeCommand("workbench.extensions.installExtension", extId);
319+
extension = vscode.extensions.getExtension(extId);
320+
break;
321+
case "Ignore":
322+
config().update("ignoreInstallServerManager", true, vscode.ConfigurationTarget.Global);
323+
break;
324+
case "Skip":
325+
default:
326+
}
327+
});
328+
}
329+
if (extension) {
330+
if (!extension.isActive) {
331+
await extension.activate();
332+
}
333+
return extension.exports;
269334
}
270-
return vscode.window
271-
.showInformationMessage(
272-
"The InterSystems® Server Manager extension is recommended to help you define connections.",
273-
"Install",
274-
"Skip",
275-
"Ignore"
276-
)
277-
.then(async (action) => {
278-
switch (action) {
279-
case "Install":
280-
await vscode.commands.executeCommand("workbench.extensions.search", `@tag:"intersystems"`);
281-
await vscode.commands.executeCommand("extension.open", extId);
282-
await vscode.commands.executeCommand("workbench.extensions.installExtension", extId);
283-
break;
284-
case "Ignore":
285-
config().update("ignoreInstallServerManager", true, vscode.ConfigurationTarget.Global);
286-
break;
287-
case "Skip":
288-
default:
289-
}
290-
});
291335
}
292336

293337
export async function activate(context: vscode.ExtensionContext): Promise<void> {
294338
if (!packageJson.version.includes("SNAPSHOT")) {
295339
reporter = new TelemetryReporter(extensionId, extensionVersion, aiKey);
296340
}
297341

342+
// Get api for servermanager extension, perhaps offering to install it
343+
serverManagerApi = await serverManager();
344+
298345
const languages = packageJson.contributes.languages.map((lang) => lang.id);
299346
workspaceState = context.workspaceState;
300347
extensionContext = context;
@@ -305,7 +352,6 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
305352
fileSystemProvider = new FileSystemProvider();
306353

307354
explorerProvider = new ObjectScriptExplorerProvider();
308-
// vscode.window.registerTreeDataProvider("ObjectScriptExplorer", explorerProvider);
309355
vscode.window.createTreeView("ObjectScriptExplorer", {
310356
treeDataProvider: explorerProvider,
311357
showCollapseAll: true,
@@ -322,7 +368,17 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
322368
panel.command = "vscode-objectscript.serverActions";
323369
panel.show();
324370

325-
checkConnection(true);
371+
// Check once (flushing cookies) each connection used by the workspace(s)
372+
const toCheck = new Map<string, vscode.Uri>();
373+
vscode.workspace.workspaceFolders.map((workspaceFolder) => {
374+
const uri = workspaceFolder.uri;
375+
const { configName } = connectionTarget(uri);
376+
toCheck.set(configName, uri);
377+
});
378+
toCheck.forEach(function (uri) {
379+
checkConnection(true, uri);
380+
});
381+
326382
vscode.workspace.onDidChangeConfiguration(({ affectsConfiguration }) => {
327383
if (affectsConfiguration("objectscript.conn")) {
328384
checkConnection(true);
@@ -597,9 +653,6 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
597653
...proposed
598654
);
599655
reporter && reporter.sendTelemetryEvent("extensionActivated");
600-
601-
// offer to install servermanager extension
602-
await serverManager();
603656
}
604657

605658
export function deactivate(): void {

0 commit comments

Comments
 (0)