Skip to content

Commit 63640cb

Browse files
Merge pull request #251 from gjsjohnmurray/do-187v2
Show `objectscript.conn.docker-compose` type connections under 'Current' node
2 parents 7e10d8a + 6013eb8 commit 63640cb

File tree

3 files changed

+103
-24
lines changed

3 files changed

+103
-24
lines changed

src/api/getServerSpec.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as vscode from "vscode";
22
import { IServerSpec } from "@intersystems-community/intersystems-servermanager";
3+
import { OBJECTSCRIPT_EXTENSIONID } from "../extension";
34

45
/**
56
* Get a server specification.
@@ -17,7 +18,42 @@ export async function getServerSpec(
1718

1819
// Unknown server
1920
if (!server) {
20-
return undefined;
21+
const folder = vscode.workspace.workspaceFolders?.find(f => f.name === name);
22+
if (!folder) {
23+
return undefined;
24+
}
25+
26+
// It is the name of a workspace root folder
27+
// Get the server details from the ObjectScript extension if available
28+
const objectScriptExtension = vscode.extensions.getExtension(OBJECTSCRIPT_EXTENSIONID);
29+
if (!objectScriptExtension) {
30+
return undefined;
31+
}
32+
if (!objectScriptExtension.isActive) {
33+
// Activating it here would cause a deadlock because the activate method of the ObjectScript extension itself calls our getServerSpec API
34+
return undefined;
35+
}
36+
let serverForUri: any;
37+
if (objectScriptExtension.exports.asyncServerForUri) {
38+
serverForUri = await objectScriptExtension.exports.asyncServerForUri(folder.uri);
39+
} else {
40+
serverForUri = objectScriptExtension.exports.serverForUri(folder.uri);
41+
}
42+
if (!serverForUri) {
43+
return undefined;
44+
}
45+
return {
46+
name: serverForUri.serverName,
47+
webServer: {
48+
scheme: serverForUri.scheme,
49+
host: serverForUri.host,
50+
port: serverForUri.port,
51+
pathPrefix: serverForUri.pathPrefix
52+
},
53+
username: serverForUri.username,
54+
password: serverForUri.password ? serverForUri.password : undefined,
55+
description: `Server for workspace folder "${name}"`,
56+
};
2157
}
2258

2359
server.name = name;

src/extension.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { logout, serverSessions } from "./makeRESTRequest";
1414
import { NamespaceTreeItem, ProjectTreeItem, ServerManagerView, ServerTreeItem, SMTreeItem, WebAppTreeItem } from "./ui/serverManagerView";
1515

1616
export const extensionId = "intersystems-community.servermanager";
17+
export const OBJECTSCRIPT_EXTENSIONID = "intersystems-community.vscode-objectscript";
18+
1719
export let globalState: vscode.Memento;
1820

1921
export function getAccountFromParts(serverName: string, userName?: string): vscode.AuthenticationSessionAccountInformation | undefined {
@@ -262,18 +264,17 @@ export function activate(context: vscode.ExtensionContext) {
262264
const namespace = pathParts[3];
263265
const serverSpec = await getServerSpec(serverName);
264266
if (serverSpec) {
265-
const ISFS_ID = "intersystems-community.vscode-objectscript";
266-
const isfsExtension = vscode.extensions.getExtension(ISFS_ID);
267+
const isfsExtension = vscode.extensions.getExtension(OBJECTSCRIPT_EXTENSIONID);
267268
if (isfsExtension) {
268269
if (!isfsExtension.isActive) {
269270
await isfsExtension.activate();
270271
if (!isfsExtension.isActive) {
271-
vscode.window.showErrorMessage(`${ISFS_ID} could not be activated.`, "Close");
272+
vscode.window.showErrorMessage(`${OBJECTSCRIPT_EXTENSIONID} could not be activated.`, "Close");
272273
return;
273274
}
274275
}
275276
} else {
276-
vscode.window.showErrorMessage(`${ISFS_ID} is not installed.`, "Close");
277+
vscode.window.showErrorMessage(`${OBJECTSCRIPT_EXTENSIONID} is not installed.`, "Close");
277278
return;
278279
}
279280

src/ui/serverManagerView.ts

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import * as vscode from "vscode";
22
import { getServerNames } from "../api/getServerNames";
33
import { getServerSpec } from "../api/getServerSpec";
44
import { getServerSummary } from "../api/getServerSummary";
5-
import { IServerName } from "@intersystems-community/intersystems-servermanager";
5+
import { IServerName, IServerSpec } from "@intersystems-community/intersystems-servermanager";
66
import { makeRESTRequest } from "../makeRESTRequest";
7+
import { OBJECTSCRIPT_EXTENSIONID } from "../extension";
78

89
const SETTINGS_VERSION = "v1";
910

@@ -271,10 +272,12 @@ function allServers(treeItem: SMTreeItem, params?: any): ServerTreeItem[] {
271272
return children;
272273
}
273274

274-
function currentServers(element: SMTreeItem, params?: any): ServerTreeItem[] {
275+
async function currentServers(element: SMTreeItem, params?: any): Promise<ServerTreeItem[]> {
275276
const children = new Map<string, ServerTreeItem>();
277+
const dockerLocalPorts = new Map<number, string>();
276278

277-
vscode.workspace.workspaceFolders?.map((folder) => {
279+
const workspaceFolders = vscode.workspace.workspaceFolders || [];
280+
await Promise.all(workspaceFolders.map(async (folder) => {
278281
const serverName = folder.uri.authority.split(":")[0];
279282
if (["isfs", "isfs-readonly"].includes(folder.uri.scheme)) {
280283
const serverSummary = getServerSummary(serverName);
@@ -284,10 +287,25 @@ function currentServers(element: SMTreeItem, params?: any): ServerTreeItem[] {
284287
new ServerTreeItem({ parent: element, label: serverName, id: serverName }, serverSummary),
285288
);
286289
}
290+
return;
287291
}
288292
const conn = vscode.workspace.getConfiguration("objectscript.conn", folder);
289293
const connServer = conn.get<string>("server");
290-
if (connServer) {
294+
if (conn.get("docker-compose")) {
295+
const objectScriptExtension = vscode.extensions.getExtension(OBJECTSCRIPT_EXTENSIONID);
296+
if (objectScriptExtension) {
297+
if (!objectScriptExtension.isActive) {
298+
await objectScriptExtension.activate();
299+
}
300+
if (objectScriptExtension.isActive && objectScriptExtension.exports?.asyncServerForUri) {
301+
const server = await objectScriptExtension.exports.asyncServerForUri(folder.uri);
302+
if (server && server.host === "localhost" && server.port > 0 && !dockerLocalPorts.has(server.port)) {
303+
dockerLocalPorts.set(server.port, folder.name);
304+
}
305+
}
306+
}
307+
}
308+
else if (connServer) {
291309
const serverSummary = getServerSummary(connServer);
292310
if (serverSummary) {
293311
children.set(
@@ -296,7 +314,18 @@ function currentServers(element: SMTreeItem, params?: any): ServerTreeItem[] {
296314
);
297315
}
298316
}
299-
});
317+
318+
}));
319+
320+
dockerLocalPorts.forEach((name, port) => {
321+
if (!children.has(name)) {
322+
const serverSummary: IServerName = { name, description: `Docker service bound to local port ${port} for folder '${name}'`, detail: `http://localhost:${port}/` };
323+
children.set(
324+
name,
325+
new ServerTreeItem({ parent: element, label: `docker:${port}`, id: name }, serverSummary),
326+
);
327+
}
328+
})
300329

301330
return Array.from(children.values()).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
302331
}
@@ -347,7 +376,7 @@ export class ServerTreeItem extends SMTreeItem {
347376
super({
348377
getChildren: serverFeatures,
349378
id: parentFolderId + ":" + serverSummary.name,
350-
label: serverSummary.name,
379+
label: element.label ? element.label : serverSummary.name,
351380
params: { serverSummary },
352381
parent: element.parent,
353382
tooltip: new vscode.MarkdownString(wrappedDetail).appendMarkdown(escapedDescription ? `\n\n*${escapedDescription}*` : ""),
@@ -377,12 +406,12 @@ async function serverFeatures(element: ServerTreeItem, params?: any): Promise<Fe
377406
const children: FeatureTreeItem[] = [];
378407

379408
if (params?.serverSummary) {
380-
const name = params.serverSummary.name;
381-
const serverSpec = await getServerSpec(name);
409+
let serverSpec = await specFromServerSummary(params.serverSummary);
382410
if (!serverSpec) {
383411
return undefined;
384412
}
385413

414+
const name = serverSpec.name;
386415
let response = await makeRESTRequest("HEAD", serverSpec);
387416
if (response?.status === 401) {
388417
// Authentication error, so retry in case first attempt cleared a no-longer-valid stored password
@@ -392,12 +421,21 @@ async function serverFeatures(element: ServerTreeItem, params?: any): Promise<Fe
392421
if (response?.status !== 200) {
393422
children.push(new OfflineTreeItem({ parent: element, label: name, id: name }, serverSpec.username || 'UnknownUser'));
394423
} else {
395-
children.push(new NamespacesTreeItem({ parent: element, label: name, id: name }, element.name, serverSpec.username || 'UnknownUser'));
424+
children.push(new NamespacesTreeItem({ parent: element, label: name, id: name }, element.name, serverSpec, serverSpec.username || 'UnknownUser'));
396425
}
397426
}
398427
return children;
399428
}
400429

430+
async function specFromServerSummary(serverSummary: IServerName): Promise<IServerSpec | undefined> {
431+
const { name, description, detail } = serverSummary;
432+
const dockerDetail = detail.match(/^http:\/\/localhost:(\d+)\/$/);
433+
if (dockerDetail) {
434+
return { name, description, webServer: { scheme: "http", host: "127.0.0.1", port: parseInt(dockerDetail[1], 10), pathPrefix: "" } };
435+
}
436+
return getServerSpec(name);
437+
}
438+
401439
// tslint:disable-next-line: max-classes-per-file
402440
export class FeatureTreeItem extends SMTreeItem {
403441
}
@@ -428,14 +466,15 @@ export class NamespacesTreeItem extends FeatureTreeItem {
428466
constructor(
429467
element: ISMItem,
430468
serverName: string,
469+
serverSpec: IServerSpec,
431470
username: string
432471
) {
433472
const parentFolderId = element.parent?.id || "";
434473
super({
435474
getChildren: serverNamespaces,
436475
id: parentFolderId + ":namespaces",
437476
label: "Namespaces",
438-
params: { serverName },
477+
params: { serverName, serverSpec },
439478
parent: element.parent,
440479
tooltip: `Namespaces '${username}' can access`,
441480
});
@@ -457,7 +496,7 @@ async function serverNamespaces(element: ServerTreeItem, params?: any): Promise<
457496

458497
if (params?.serverName) {
459498
const name: string = params.serverName;
460-
const serverSpec = await getServerSpec(name);
499+
const serverSpec: IServerSpec | undefined = params.serverSpec;
461500
if (!serverSpec) {
462501
return undefined;
463502
}
@@ -468,7 +507,7 @@ async function serverNamespaces(element: ServerTreeItem, params?: any): Promise<
468507
} else {
469508
const serverApiVersion = response.data.result.content.api;
470509
response.data.result.content.namespaces.map((namespace: string) => {
471-
children.push(new NamespaceTreeItem({ parent: element, label: name, id: name }, namespace, name, serverApiVersion));
510+
children.push(new NamespaceTreeItem({ parent: element, label: name, id: name }, namespace, name, serverSpec, serverApiVersion));
472511
});
473512
}
474513
}
@@ -483,6 +522,7 @@ export class NamespaceTreeItem extends SMTreeItem {
483522
element: ISMItem,
484523
name: string,
485524
serverName: string,
525+
serverSpec: IServerSpec,
486526
serverApiVersion: number
487527
) {
488528
const parentFolderId = element.parent?.id || "";
@@ -493,7 +533,7 @@ export class NamespaceTreeItem extends SMTreeItem {
493533
parent: element.parent,
494534
tooltip: `${name} on ${serverName}`,
495535
getChildren: namespaceFeatures,
496-
params: { serverName, serverApiVersion }
536+
params: { serverName, serverSpec, serverApiVersion }
497537
});
498538
this.name = name;
499539
this.contextValue = `${serverApiVersion.toString()}/${name === "%SYS" ? "sysnamespace" : "namespace"}`;
@@ -510,8 +550,8 @@ export class NamespaceTreeItem extends SMTreeItem {
510550
*/
511551
async function namespaceFeatures(element: NamespaceTreeItem, params?: any): Promise<FeatureTreeItem[] | undefined> {
512552
return [
513-
new ProjectsTreeItem({ parent: element, id: element.name, label: element.name }, params.serverName, params.serverApiVersion),
514-
new WebAppsTreeItem({ parent: element, id: element.name, label: element.name }, params.serverName, params.serverApiVersion)
553+
new ProjectsTreeItem({ parent: element, id: element.name, label: element.name }, params.serverName, params.serverSpec, params.serverApiVersion),
554+
new WebAppsTreeItem({ parent: element, id: element.name, label: element.name }, params.serverName, params.serverSpec, params.serverApiVersion)
515555
];
516556
}
517557

@@ -520,6 +560,7 @@ export class ProjectsTreeItem extends FeatureTreeItem {
520560
constructor(
521561
element: ISMItem,
522562
serverName: string,
563+
serverSpec: IServerSpec,
523564
serverApiVersion: number
524565
) {
525566
const parentFolderId = element.parent?.id || '';
@@ -529,7 +570,7 @@ export class ProjectsTreeItem extends FeatureTreeItem {
529570
id: parentFolderId + ':projects',
530571
tooltip: `Projects in this namespace`,
531572
getChildren: namespaceProjects,
532-
params: { serverName, serverApiVersion, ns: element.label }
573+
params: { serverName, serverSpec, serverApiVersion, ns: element.label }
533574
});
534575
this.name = 'Projects';
535576
this.contextValue = serverApiVersion.toString() + '/projects';
@@ -549,7 +590,7 @@ async function namespaceProjects(element: ProjectsTreeItem, params?: any): Promi
549590

550591
if (params?.serverName && params.ns) {
551592
const name: string = params.serverName;
552-
const serverSpec = await getServerSpec(name)
593+
const serverSpec: IServerSpec | undefined = params.serverSpec;
553594
if (!serverSpec) {
554595
return undefined
555596
}
@@ -607,6 +648,7 @@ export class WebAppsTreeItem extends FeatureTreeItem {
607648
constructor(
608649
element: ISMItem,
609650
serverName: string,
651+
serverSpec: IServerSpec,
610652
serverApiVersion: number
611653
) {
612654
const parentFolderId = element.parent?.id || '';
@@ -616,7 +658,7 @@ export class WebAppsTreeItem extends FeatureTreeItem {
616658
id: parentFolderId + ':webapps',
617659
tooltip: `Web Applications in this namespace`,
618660
getChildren: namespaceWebApps,
619-
params: { serverName, serverApiVersion, ns: element.label }
661+
params: { serverName, serverSpec, serverApiVersion, ns: element.label }
620662
});
621663
this.name = 'Web Applications';
622664
this.contextValue = serverApiVersion.toString() + '/webapps';
@@ -636,7 +678,7 @@ async function namespaceWebApps(element: ProjectsTreeItem, params?: any): Promis
636678

637679
if (params?.serverName && params.ns) {
638680
const name: string = params.serverName;
639-
const serverSpec = await getServerSpec(name)
681+
const serverSpec: IServerSpec | undefined = params.serverSpec;
640682
if (!serverSpec) {
641683
return undefined
642684
}

0 commit comments

Comments
 (0)