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 all 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
# Change Log

## [0.2.0] 05-Feb-2025
- Enhancements
- Support use of Server Manager connection definitions (#60)
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,14 @@ Try out the [SQL QuickStart](https://gettingstarted.intersystems.com/language-qu
- If you have no previous database connections, you will see an "Add new connection" button. Click that.
![Add connection button](https://raw.githubusercontent.com/intersystems-community/sqltools-intersystems-driver/master/docs/assets/img/addconnection.png)
- 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)
- Click InterSystems IRIS
- Fill out connection information
- Test the connection
- Save the connection
- Click InterSystems IRIS.
- Fill out connection information:
- Namespace to work in.
- Connect using: "Server Definition" or "Server and Port"
- 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.
- For "Server and Port", provide (as applicable) "Webserver address", "Webserver port", "Path prefix (for shared webserver)", "Use HTTPS", "Username", "Ask for password?" and "Password".
- Test the connection.
- Save the connection.

## Use

Expand Down
20 changes: 17 additions & 3 deletions connection.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
"title": "Connect using",
"type": "string",
"minLength": 1,
"enum": ["Server and Port"],
"default": "Server and Port"
"enum": ["Server Definition", "Server and Port"],
"default": "Server Definition"
},
"showSystem": {
"title": "Show system items?",
Expand Down Expand Up @@ -75,14 +75,28 @@
"default": ""
},
"https": {
"title": "Use HTTPS",
"title": "Use HTTPS?",
"default": false,
"type": "boolean"
},
"username": { "$ref": "#/definitions/username" },
"askForPassword": { "$ref": "#/definitions/askForPassword" }
},
"required": ["server", "port", "username"]
},
{
"properties": {
"connectionMethod": {
"enum": ["Server Definition"]
},
"serverName": {
"title": "Server name",
"type": "string",
"minLength": 1,
"description": "Name of a server definition configured using InterSystems Server Manager"
}
},
"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