Skip to content

Commit 2ac5fea

Browse files
Merge pull request #60 from gjsjohnmurray/do-8
Support definitions that reference `intersystems.servers` entries
2 parents 4841a70 + 2e8cfdb commit 2ac5fea

8 files changed

+175
-60
lines changed

.github/workflows/main.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,12 @@ jobs:
5858
if: runner.os == 'Linux'
5959
run: |
6060
npx vsce package -o ${{ steps.set-version.outputs.name }}.vsix
61-
- uses: actions/upload-artifact@v2
61+
- uses: actions/upload-artifact@v4
6262
if: runner.os == 'Linux'
6363
with:
6464
name: ${{ steps.set-version.outputs.name }}.vsix
6565
path: ${{ steps.set-version.outputs.name }}.vsix
66-
- uses: actions/upload-artifact@v2
66+
- uses: actions/upload-artifact@v4
6767
if: runner.os == 'Linux'
6868
with:
6969
name: meta

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
# Change Log
2+
3+
## [0.2.0] 05-Feb-2025
4+
- Enhancements
5+
- Support use of Server Manager connection definitions (#60)

README.md

+8-4
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,14 @@ Try out the [SQL QuickStart](https://gettingstarted.intersystems.com/language-qu
3030
- If you have no previous database connections, you will see an "Add new connection" button. Click that.
3131
![Add connection button](https://raw.githubusercontent.com/intersystems-community/sqltools-intersystems-driver/master/docs/assets/img/addconnection.png)
3232
- If you already have other connections defined, you won't see the button. Instead, open the command palette (Ctrl/Cmd+Shift+P) and run "SQLTools Management: Add New Connection" ![Add connection from command palette](https://raw.githubusercontent.com/intersystems-community/sqltools-intersystems-driver/master/docs/assets/img/command_palette_add_new.png)
33-
- Click InterSystems IRIS
34-
- Fill out connection information
35-
- Test the connection
36-
- Save the connection
33+
- Click InterSystems IRIS.
34+
- Fill out connection information:
35+
- Namespace to work in.
36+
- Connect using: "Server Definition" or "Server and Port"
37+
- For "Server Definition", provide in "Server name" the name of a server configured using the InterSystems Server Manager extension. These server specs are stored in the `intersystems.servers` settings object.
38+
- For "Server and Port", provide (as applicable) "Webserver address", "Webserver port", "Path prefix (for shared webserver)", "Use HTTPS", "Username", "Ask for password?" and "Password".
39+
- Test the connection.
40+
- Save the connection.
3741

3842
## Use
3943

connection.schema.json

+17-3
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030
"title": "Connect using",
3131
"type": "string",
3232
"minLength": 1,
33-
"enum": ["Server and Port"],
34-
"default": "Server and Port"
33+
"enum": ["Server Definition", "Server and Port"],
34+
"default": "Server Definition"
3535
},
3636
"showSystem": {
3737
"title": "Show system items?",
@@ -75,14 +75,28 @@
7575
"default": ""
7676
},
7777
"https": {
78-
"title": "Use HTTPS",
78+
"title": "Use HTTPS?",
7979
"default": false,
8080
"type": "boolean"
8181
},
8282
"username": { "$ref": "#/definitions/username" },
8383
"askForPassword": { "$ref": "#/definitions/askForPassword" }
8484
},
8585
"required": ["server", "port", "username"]
86+
},
87+
{
88+
"properties": {
89+
"connectionMethod": {
90+
"enum": ["Server Definition"]
91+
},
92+
"serverName": {
93+
"title": "Server name",
94+
"type": "string",
95+
"minLength": 1,
96+
"description": "Name of a server definition configured using InterSystems Server Manager"
97+
}
98+
},
99+
"required": ["serverName"]
86100
}
87101
]
88102
},

package-lock.json

+26-15
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
"name": "sqltools-intersystems-driver",
33
"displayName": "SQLTools InterSystems IRIS",
44
"description": "SQLTools Driver for InterSystems IRIS",
5-
"version": "0.1.8-SNAPSHOT",
5+
"version": "0.2.0-SNAPSHOT",
66
"engines": {
7-
"vscode": "^1.66.0"
7+
"vscode": "^1.93.0"
88
},
99
"publisher": "intersystems-community",
1010
"license": "MIT",
@@ -67,8 +67,9 @@
6767
},
6868
"devDependencies": {
6969
"@babel/preset-env": "^7.14.2",
70+
"@intersystems-community/intersystems-servermanager": "^3.8.0",
7071
"@types/node": "^14.17.0",
71-
"@types/vscode": "^1.66.0",
72+
"@types/vscode": "^1.93.0",
7273
"@vscode/vsce": "^2.19.0",
7374
"rimraf": "^3.0.2",
7475
"ts-loader": "^9.2.1",

src/extension.ts

+104-19
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ const { publisher, name } = require('../package.json');
66
// import { workspace } from 'vscode';
77
// import { Uri } from 'vscode';
88
// import path from 'path';
9+
import * as serverManager from "@intersystems-community/intersystems-servermanager";
10+
11+
const smExtensionId = "intersystems-community.servermanager";
12+
let serverManagerApi: serverManager.ServerManagerAPI;
13+
14+
/** Map of the intersystems.server connection specs we have resolved via the API to that extension */
15+
const resolvedConnSpecs = new Map<string, serverManager.IServerSpec>();
916

1017
const driverName = 'InterSystems IRIS Driver';
1118

@@ -45,35 +52,113 @@ export async function activate(extContext: ExtensionContext): Promise<IDriverExt
4552
driverName,
4653
parseBeforeSaveConnection: ({ connInfo }) => {
4754
/**
48-
* This hook is called before saving the connecton using the assistant
49-
* so you can do any transformations before saving it to disk.active
50-
* EG: relative file path transformation, string manipulation etc
51-
* Below is the exmaple for SQLite, where we save the DB path relative to workspace
52-
* and later we transform it back to absolute before editing
55+
* This hook is called before saving the connection using the assistant
56+
* so you can do any transformations before saving it
5357
*/
54-
// if (path.isAbsolute(connInfo.database)) {
55-
// const databaseUri = Uri.file(connInfo.database);
56-
// const dbWorkspace = workspace.getWorkspaceFolder(databaseUri);
57-
// if (dbWorkspace) {
58-
// connInfo.database = `\$\{workspaceFolder:${dbWorkspace.name}\}/${workspace.asRelativePath(connInfo.database, false)}`;
59-
// }
60-
// }
58+
if (connInfo.connectionMethod === 'Server Definition') {
59+
// Transform to a connectString property
60+
connInfo.connectString = `${connInfo.serverName}:${connInfo.namespace}`;
61+
connInfo.serverName = undefined;
62+
connInfo.namespace = undefined;
63+
// Remove properties carried over from 'Server and Port' type connection
64+
connInfo.server = undefined;
65+
connInfo.port = undefined;
66+
connInfo.pathPrefix = undefined;
67+
connInfo.https = undefined;
68+
connInfo.askForPassword = undefined;
69+
connInfo.username = undefined;
70+
connInfo.password = undefined;
71+
72+
}
6173
return connInfo;
6274
},
6375
parseBeforeEditConnection: ({ connInfo }) => {
6476
/**
65-
* This hook is called before editing the connecton using the assistant
77+
* This hook is called before editing the connection using the assistant
6678
* so you can do any transformations before editing it.
6779
* EG: absolute file path transformation, string manipulation etc
6880
* Below is the exmaple for SQLite, where we use relative path to save,
6981
* but we transform to asolute before editing
7082
*/
71-
// if (!path.isAbsolute(connInfo.database) && /\$\{workspaceFolder:(.+)}/g.test(connInfo.database)) {
72-
// const workspaceName = connInfo.database.match(/\$\{workspaceFolder:(.+)}/)[1];
73-
// const dbWorkspace = workspace.workspaceFolders.find(w => w.name === workspaceName);
74-
// if (dbWorkspace)
75-
// connInfo.database = path.resolve(dbWorkspace.uri.fsPath, connInfo.database.replace(/\$\{workspaceFolder:(.+)}/g, './'));
76-
// }
83+
if (connInfo.connectionMethod === 'Server Definition') {
84+
const connParts = connInfo.connectString.split(':');
85+
connInfo.serverName = connParts[0];
86+
connInfo.namespace = connParts[1];
87+
}
88+
return connInfo;
89+
},
90+
resolveConnection: async ({ connInfo }) => {
91+
/**
92+
* This hook is called after a connection definition has been fetched
93+
* from settings and is about to be used to connect.
94+
*/
95+
if (connInfo.connectionMethod === 'Server Definition') {
96+
const connParts = connInfo.connectString.split(':');
97+
const serverName = connParts[0];
98+
const namespace = connParts[1];
99+
let connSpec = resolvedConnSpecs.get(serverName)
100+
if (!connSpec) {
101+
102+
if (!serverManagerApi) {
103+
104+
// Get api for servermanager extension
105+
const smExt = vscode.extensions.getExtension(smExtensionId);
106+
if (!smExt) {
107+
throw new Error("Server Manager extension not found");
108+
}
109+
if (!smExt.isActive) await smExt.activate();
110+
serverManagerApi = smExt.exports;
111+
}
112+
connSpec = await serverManagerApi.getServerSpec(serverName);
113+
if (!connSpec) {
114+
throw new Error(`Failed to fetch definition of server '${serverName}'`)
115+
}
116+
const isUnauthenticated = (username?: string): boolean => {
117+
return username && (username == "" || username.toLowerCase() == "unknownuser");
118+
}
119+
const resolvePassword = async (serverSpec): Promise<void> => {
120+
if (
121+
// Connection isn't unauthenticated
122+
(!isUnauthenticated(serverSpec.username)) &&
123+
// A password is missing
124+
typeof serverSpec.password == "undefined"
125+
) {
126+
const scopes = [serverSpec.name, serverSpec.username || ""];
127+
128+
// Handle Server Manager extension version < 3.8.0
129+
const account = serverManagerApi.getAccount ? serverManagerApi.getAccount(serverSpec) : undefined;
130+
131+
let session = await vscode.authentication.getSession(serverManager.AUTHENTICATION_PROVIDER, scopes, {
132+
silent: true,
133+
account,
134+
});
135+
if (!session) {
136+
session = await vscode.authentication.getSession(serverManager.AUTHENTICATION_PROVIDER, scopes, {
137+
createIfNone: true,
138+
account,
139+
});
140+
}
141+
if (session) {
142+
// If original spec lacked username use the one obtained by the authprovider
143+
serverSpec.username = serverSpec.username || session.scopes[1];
144+
serverSpec.password = session.accessToken;
145+
}
146+
}
147+
}
148+
149+
await resolvePassword(connSpec);
150+
resolvedConnSpecs.set(serverName, connSpec);
151+
}
152+
connInfo = { ...connInfo,
153+
https: connSpec.webServer.scheme === 'https',
154+
server: connSpec.webServer.host,
155+
port: connSpec.webServer.port,
156+
pathPrefix: connSpec.webServer.pathPrefix || '',
157+
username: connSpec.username,
158+
password: connSpec.password,
159+
namespace,
160+
}
161+
}
77162
return connInfo;
78163
},
79164
driverAliases: DRIVER_ALIASES,

src/ls/driver.ts

+10-14
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,16 @@ export default class IRISDriver extends AbstractDriver<IRISdb, DriverOptions> im
2525
this.showSystem = this.credentials.showSystem || false;
2626
this.filter = this.credentials.filter || "";
2727

28-
if (this.credentials.serverName) {
29-
throw new Error("not supported");
30-
} else {
31-
let { https, server: host, port, pathPrefix, username, password } = this.credentials;
32-
config = {
33-
https,
34-
host,
35-
port,
36-
pathPrefix,
37-
namespace,
38-
username,
39-
password
40-
};
41-
}
28+
let { https, server: host, port, pathPrefix, username, password } = this.credentials;
29+
config = {
30+
https,
31+
host,
32+
port,
33+
pathPrefix,
34+
namespace,
35+
username,
36+
password
37+
};
4238

4339
const irisdb = new IRISdb(config);
4440
return irisdb.open()

0 commit comments

Comments
 (0)