Skip to content

Commit

Permalink
Merge pull request #422 from KxSystems/KXI-52817
Browse files Browse the repository at this point in the history
KXI-52817 Add import connections
  • Loading branch information
Philip-Carneiro-KX authored Sep 9, 2024
2 parents 52cf42d + c8a4334 commit 3c4f6f5
Show file tree
Hide file tree
Showing 5 changed files with 340 additions and 6 deletions.
101 changes: 100 additions & 1 deletion src/commands/serverCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import { InsightsConnection } from "../classes/insightsConnection";
import { MetaContentProvider } from "../services/metaContentProvider";
import { handleLabelsConnMap, removeConnFromLabels } from "../utils/connLabel";
import {
ExportedConnections,
InsightDetails,
Insights,
Server,
Expand Down Expand Up @@ -376,7 +377,7 @@ export async function addKdbConnection(
tls: kdbData.tls,
},
};
if (servers[0].managed) {
if (servers.key.managed) {
await addLocalConnectionContexts(getServerName(servers[0]));
}
} else {
Expand Down Expand Up @@ -539,6 +540,95 @@ export async function editKdbConnection(
}
}

// test fs readFileSync unit tests are flaky, no correct way to test them
/* istanbul ignore next */
export async function importConnections() {
const options = {
canSelectMany: false,
openLabel: "Select JSON File",
filters: {
"JSON Files": ["json"],
"All Files": ["*"],
},
};

const fileUri = await window.showOpenDialog(options);
if (!fileUri || fileUri.length === 0) {
kdbOutputLog("[IMPORT CONNECTION]No file selected", "ERROR");
return;
}
const filePath = fileUri[0].fsPath;
const fileContent = fs.readFileSync(filePath, "utf8");

let importedConnections: ExportedConnections;
try {
importedConnections = JSON.parse(fileContent);
} catch (e) {
kdbOutputLog("[IMPORT CONNECTION]Invalid JSON format", "ERROR");
return;
}

if (!isValidExportedConnections(importedConnections)) {
kdbOutputLog(
"[IMPORT CONNECTION]JSON does not match the required format",
"ERROR",
);
return;
}
if (
importedConnections.connections.KDB.length === 0 &&
importedConnections.connections.Insights.length === 0
) {
kdbOutputLog(
"[IMPORT CONNECTION]There is no KDB or Insights connections to import in this JSON file",
"ERROR",
);
return;
}
await addImportedConnections(importedConnections);
}

export async function addImportedConnections(
importedConnections: ExportedConnections,
) {
const connMangService = new ConnectionManagementService();
const existingAliases = connMangService.retrieveListOfConnectionsNames();
const localAlreadyExists = existingAliases.has("local");
let counter = 1;
for (const connection of importedConnections.connections.Insights) {
let alias = connMangService.checkConnAlias(connection.alias, true);

while (existingAliases.has(alias)) {
alias = `${connection.alias}-${counter}`;
counter++;
}
connection.alias = alias;
await addInsightsConnection(connection);
existingAliases.add(alias);
counter = 1;
}

for (const connection of importedConnections.connections.KDB) {
let alias = connMangService.checkConnAlias(
connection.serverAlias,
false,
localAlreadyExists,
);
while (existingAliases.has(alias)) {
alias = `${connection.serverAlias}-${counter}`;
counter++;
}
const isManaged = alias === "local";
connection.serverAlias = alias;
await addKdbConnection(connection, isManaged);
existingAliases.add(alias);
counter = 1;
}

kdbOutputLog("[IMPORT CONNECTION]Connections imported successfully", "INFO");
window.showInformationMessage("Connections imported successfully");
}

export async function removeConnection(viewItem: KdbNode | InsightsNode) {
const connMngService = new ConnectionManagementService();
removeConnFromLabels(
Expand Down Expand Up @@ -1058,3 +1148,12 @@ export function writeScratchpadResult(
}
}
}

function isValidExportedConnections(data: any): data is ExportedConnections {
return (
data &&
data.connections &&
Array.isArray(data.connections.Insights) &&
Array.isArray(data.connections.KDB)
);
}
5 changes: 3 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
editKdbConnection,
enableTLS,
exportConnections,
importConnections,
openMeta,
refreshGetMeta,
removeConnection,
Expand Down Expand Up @@ -281,8 +282,8 @@ export async function activate(context: ExtensionContext) {
exportConnections(viewItem.label);
},
),
commands.registerCommand("kdb.connections.import", () => {
window.showInformationMessage("Import Connections command executed");
commands.registerCommand("kdb.connections.import", async () => {
await importConnections();
}),
commands.registerCommand(
"kdb.open.meta",
Expand Down
24 changes: 24 additions & 0 deletions src/services/connectionManagerService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,30 @@ export class ConnectionManagementService {
);
}

public retrieveListOfConnectionsNames(): Set<string> {
return new Set(
ext.connectionsList.map((conn) => {
if (conn instanceof KdbNode) {
return conn.details.serverAlias;
} else {
return conn.details.alias;
}
}),
);
}

public checkConnAlias(
alias: string,
isInsights: boolean,
localAlreadyExists?: boolean,
): string {
if (isInsights) {
return alias !== "local" ? alias : "localInsights";
} else {
return localAlreadyExists && alias === "local" ? alias + "KDB" : alias;
}
}

public isKdbConnection(connection: KdbNode | InsightsNode): boolean {
return connection instanceof KdbNode;
}
Expand Down
176 changes: 175 additions & 1 deletion test/suite/commands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
* specific language governing permissions and limitations under the License.
*/

import * as fs from "fs";
import assert from "assert";
import mock from "mock-fs";
import * as sinon from "sinon";
import * as vscode from "vscode";
import * as fs from "fs";
import * as dataSourceCommand from "../../src/commands/dataSourceCommand";
import * as installTools from "../../src/commands/installTools";
import * as serverCommand from "../../src/commands/serverCommand";
Expand Down Expand Up @@ -53,6 +53,7 @@ import { GetDataError } from "../../src/models/data";
import * as clientCommand from "../../src/commands/clientCommands";
import { LanguageClient } from "vscode-languageclient/node";
import {
ExportedConnections,
InsightDetails,
ServerDetails,
ServerType,
Expand Down Expand Up @@ -1112,6 +1113,179 @@ describe("serverCommand", () => {
});
});

describe("importConnections", () => {
let showOpenDialogStub: sinon.SinonStub;
let kdbOutputLogStub: sinon.SinonStub;
let addImportedConnectionsStub: sinon.SinonStub;

beforeEach(() => {
showOpenDialogStub = sinon.stub(vscode.window, "showOpenDialog");
kdbOutputLogStub = sinon.stub(coreUtils, "kdbOutputLog");
addImportedConnectionsStub = sinon.stub(
serverCommand,
"addImportedConnections",
);
});

afterEach(() => {
sinon.restore();
mock.restore();
});

it("should log an error if no file is selected", async () => {
showOpenDialogStub.resolves(undefined);

await serverCommand.importConnections();

assert(
kdbOutputLogStub.calledWith(
"[IMPORT CONNECTION]No file selected",
"ERROR",
),
);
});
});

describe("addImportedConnections", async () => {
let addInsightsConnectionStub: sinon.SinonStub;
let addKdbConnectionStub: sinon.SinonStub;
let kdbOutputLogStub: sinon.SinonStub;
let showInformationMessageStub: sinon.SinonStub;
let getInsightsStub: sinon.SinonStub;
let getServersStub: sinon.SinonStub;
const kdbNodeImport1: KdbNode = {
label: "local",
details: {
serverName: "testKdb",
serverAlias: "local",
serverPort: "1818",
auth: false,
managed: false,
tls: false,
},
collapsibleState: vscode.TreeItemCollapsibleState.None,
contextValue: "kdbNode",
children: [],
getTooltip: function (): vscode.MarkdownString {
throw new Error("Function not implemented.");
},
getDescription: function (): string {
throw new Error("Function not implemented.");
},
iconPath: undefined,
};
const insightsNodeImport1: InsightsNode = {
label: "testInsight",
details: {
server: "testInsight",
alias: "testInsight",
auth: false,
},
collapsibleState: vscode.TreeItemCollapsibleState.None,
contextValue: "insightsNode",
children: [],
getTooltip: function (): vscode.MarkdownString {
throw new Error("Function not implemented.");
},
getDescription: function (): string {
throw new Error("Function not implemented.");
},
iconPath: undefined,
};

beforeEach(() => {
addInsightsConnectionStub = sinon.stub(
serverCommand,
"addInsightsConnection",
);
addKdbConnectionStub = sinon.stub(serverCommand, "addKdbConnection");
kdbOutputLogStub = sinon.stub(coreUtils, "kdbOutputLog");
getInsightsStub = sinon.stub(coreUtils, "getInsights").returns(undefined);
getServersStub = sinon.stub(coreUtils, "getServers").returns(undefined);
showInformationMessageStub = sinon.stub(
vscode.window,
"showInformationMessage",
);
ext.connectionsList.length = 0;
});

afterEach(() => {
sinon.restore();
ext.connectionsList.length = 0;
});

it("should add insights connections with unique aliases", async () => {
ext.connectionsList.push(insightsNodeImport1, kdbNodeImport1);
const importedConnections: ExportedConnections = {
connections: {
Insights: [
{
alias: "testImportInsights1",
server: "testInsight",
auth: false,
},
{
alias: "testImportInsights1",
server: "testInsight2",
auth: false,
},
],
KDB: [],
},
};

await serverCommand.addImportedConnections(importedConnections);

sinon.assert.notCalled(addKdbConnectionStub);
});

it("should log success message and show information message", async () => {
const importedConnections: ExportedConnections = {
connections: {
Insights: [],
KDB: [],
},
};

await serverCommand.addImportedConnections(importedConnections);

assert(
kdbOutputLogStub.calledWith(
"[IMPORT CONNECTION]Connections imported successfully",
"INFO",
),
);
assert(
showInformationMessageStub.calledWith(
"Connections imported successfully",
),
);
});

it("should add kdb connections", () => {
ext.connectionsList.push(insightsNodeImport1, kdbNodeImport1);
const importedConnections: ExportedConnections = {
connections: {
Insights: [],
KDB: [
{
serverName: "testKdb",
serverAlias: "testKdb",
serverPort: "1818",
auth: false,
managed: false,
tls: false,
},
],
},
};

serverCommand.addImportedConnections(importedConnections);

sinon.assert.notCalled(addInsightsConnectionStub);
});
});

describe("writeQueryResultsToView", () => {
it("should call executeCommand with correct arguments", () => {
const result = { data: [1, 2, 3] };
Expand Down
Loading

0 comments on commit 3c4f6f5

Please sign in to comment.