Skip to content

Support definitions that reference intersystems.servers entries #60

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,12 @@ jobs:
if: runner.os == 'Linux'
run: |
npx vsce package -o ${{ steps.set-version.outputs.name }}.vsix
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v4
if: runner.os == 'Linux'
with:
name: ${{ steps.set-version.outputs.name }}.vsix
path: ${{ steps.set-version.outputs.name }}.vsix
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v4
if: runner.os == 'Linux'
with:
name: meta
Expand Down
15 changes: 14 additions & 1 deletion connection.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"title": "Connect using",
"type": "string",
"minLength": 1,
"enum": ["Server and Port"],
"enum": ["Server and Port", "Server Definition"],
"default": "Server and Port"
},
"showSystem": {
Expand Down Expand Up @@ -83,6 +83,19 @@
"askForPassword": { "$ref": "#/definitions/askForPassword" }
},
"required": ["server", "port", "username"]
},
{
"properties": {
"connectionMethod": {
"enum": ["Server Definition"]
},
"serverName": {
"title": "Server name",
"type": "string",
"minLength": 1
}
},
"required": ["serverName"]
}
]
},
Expand Down
41 changes: 26 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"name": "sqltools-intersystems-driver",
"displayName": "SQLTools InterSystems IRIS",
"description": "SQLTools Driver for InterSystems IRIS",
"version": "0.1.8-SNAPSHOT",
"version": "0.2.0-SNAPSHOT",
"engines": {
"vscode": "^1.66.0"
"vscode": "^1.93.0"
},
"publisher": "intersystems-community",
"license": "MIT",
Expand Down Expand Up @@ -67,8 +67,9 @@
},
"devDependencies": {
"@babel/preset-env": "^7.14.2",
"@intersystems-community/intersystems-servermanager": "^3.8.0",
"@types/node": "^14.17.0",
"@types/vscode": "^1.66.0",
"@types/vscode": "^1.93.0",
"@vscode/vsce": "^2.19.0",
"rimraf": "^3.0.2",
"ts-loader": "^9.2.1",
Expand Down
123 changes: 104 additions & 19 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ const { publisher, name } = require('../package.json');
// import { workspace } from 'vscode';
// import { Uri } from 'vscode';
// import path from 'path';
import * as serverManager from "@intersystems-community/intersystems-servermanager";

const smExtensionId = "intersystems-community.servermanager";
let serverManagerApi: serverManager.ServerManagerAPI;

/** Map of the intersystems.server connection specs we have resolved via the API to that extension */
const resolvedConnSpecs = new Map<string, serverManager.IServerSpec>();

const driverName = 'InterSystems IRIS Driver';

Expand Down Expand Up @@ -45,35 +52,113 @@ export async function activate(extContext: ExtensionContext): Promise<IDriverExt
driverName,
parseBeforeSaveConnection: ({ connInfo }) => {
/**
* This hook is called before saving the connecton using the assistant
* so you can do any transformations before saving it to disk.active
* EG: relative file path transformation, string manipulation etc
* Below is the exmaple for SQLite, where we save the DB path relative to workspace
* and later we transform it back to absolute before editing
* This hook is called before saving the connection using the assistant
* so you can do any transformations before saving it
*/
// if (path.isAbsolute(connInfo.database)) {
// const databaseUri = Uri.file(connInfo.database);
// const dbWorkspace = workspace.getWorkspaceFolder(databaseUri);
// if (dbWorkspace) {
// connInfo.database = `\$\{workspaceFolder:${dbWorkspace.name}\}/${workspace.asRelativePath(connInfo.database, false)}`;
// }
// }
if (connInfo.connectionMethod === 'Server Definition') {
// Transform to a connectString property
connInfo.connectString = `${connInfo.serverName}:${connInfo.namespace}`;
connInfo.serverName = undefined;
connInfo.namespace = undefined;
// Remove properties carried over from 'Server and Port' type connection
connInfo.server = undefined;
connInfo.port = undefined;
connInfo.pathPrefix = undefined;
connInfo.https = undefined;
connInfo.askForPassword = undefined;
connInfo.username = undefined;
connInfo.password = undefined;

}
return connInfo;
},
parseBeforeEditConnection: ({ connInfo }) => {
/**
* This hook is called before editing the connecton using the assistant
* This hook is called before editing the connection using the assistant
* so you can do any transformations before editing it.
* EG: absolute file path transformation, string manipulation etc
* Below is the exmaple for SQLite, where we use relative path to save,
* but we transform to asolute before editing
*/
// if (!path.isAbsolute(connInfo.database) && /\$\{workspaceFolder:(.+)}/g.test(connInfo.database)) {
// const workspaceName = connInfo.database.match(/\$\{workspaceFolder:(.+)}/)[1];
// const dbWorkspace = workspace.workspaceFolders.find(w => w.name === workspaceName);
// if (dbWorkspace)
// connInfo.database = path.resolve(dbWorkspace.uri.fsPath, connInfo.database.replace(/\$\{workspaceFolder:(.+)}/g, './'));
// }
if (connInfo.connectionMethod === 'Server Definition') {
const connParts = connInfo.connectString.split(':');
connInfo.serverName = connParts[0];
connInfo.namespace = connParts[1];
}
return connInfo;
},
resolveConnection: async ({ connInfo }) => {
/**
* This hook is called after a connection definition has been fetched
* from settings and is about to be used to connect.
*/
if (connInfo.connectionMethod === 'Server Definition') {
const connParts = connInfo.connectString.split(':');
const serverName = connParts[0];
const namespace = connParts[1];
let connSpec = resolvedConnSpecs.get(serverName)
if (!connSpec) {

if (!serverManagerApi) {

// Get api for servermanager extension
const smExt = vscode.extensions.getExtension(smExtensionId);
if (!smExt) {
throw new Error("Server Manager extension not found");
}
if (!smExt.isActive) await smExt.activate();
serverManagerApi = smExt.exports;
}
connSpec = await serverManagerApi.getServerSpec(serverName);
if (!connSpec) {
throw new Error(`Failed to fetch definition of server '${serverName}'`)
}
const isUnauthenticated = (username?: string): boolean => {
return username && (username == "" || username.toLowerCase() == "unknownuser");
}
const resolvePassword = async (serverSpec): Promise<void> => {
if (
// Connection isn't unauthenticated
(!isUnauthenticated(serverSpec.username)) &&
// A password is missing
typeof serverSpec.password == "undefined"
) {
const scopes = [serverSpec.name, serverSpec.username || ""];

// Handle Server Manager extension version < 3.8.0
const account = serverManagerApi.getAccount ? serverManagerApi.getAccount(serverSpec) : undefined;

let session = await vscode.authentication.getSession(serverManager.AUTHENTICATION_PROVIDER, scopes, {
silent: true,
account,
});
if (!session) {
session = await vscode.authentication.getSession(serverManager.AUTHENTICATION_PROVIDER, scopes, {
createIfNone: true,
account,
});
}
if (session) {
// If original spec lacked username use the one obtained by the authprovider
serverSpec.username = serverSpec.username || session.scopes[1];
serverSpec.password = session.accessToken;
}
}
}

await resolvePassword(connSpec);
resolvedConnSpecs.set(serverName, connSpec);
}
connInfo = { ...connInfo,
https: connSpec.webServer.scheme === 'https',
server: connSpec.webServer.host,
port: connSpec.webServer.port,
pathPrefix: connSpec.webServer.pathPrefix || '',
username: connSpec.username,
password: connSpec.password,
namespace,
}
}
return connInfo;
},
driverAliases: DRIVER_ALIASES,
Expand Down
24 changes: 10 additions & 14 deletions src/ls/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,16 @@ export default class IRISDriver extends AbstractDriver<IRISdb, DriverOptions> im
this.showSystem = this.credentials.showSystem || false;
this.filter = this.credentials.filter || "";

if (this.credentials.serverName) {
throw new Error("not supported");
} else {
let { https, server: host, port, pathPrefix, username, password } = this.credentials;
config = {
https,
host,
port,
pathPrefix,
namespace,
username,
password
};
}
let { https, server: host, port, pathPrefix, username, password } = this.credentials;
config = {
https,
host,
port,
pathPrefix,
namespace,
username,
password
};

const irisdb = new IRISdb(config);
return irisdb.open()
Expand Down
Loading