From 5cb38d3260ed44f13f1102355184769a63837f8d Mon Sep 17 00:00:00 2001 From: Philip Carneiro Date: Wed, 4 Sep 2024 08:14:06 +0100 Subject: [PATCH 1/5] add import/export options to elipse icon --- package.json | 20 ++++++++++++++++++++ src/extension.ts | 6 ++++++ 2 files changed, 26 insertions(+) diff --git a/package.json b/package.json index b209bd55..66b63a16 100644 --- a/package.json +++ b/package.json @@ -200,6 +200,16 @@ } }, "commands": [ + { + "category": "KX", + "command": "kdb.connections.export", + "title": "Export connections" + }, + { + "category": "KX", + "command": "kdb.connections.import", + "title": "Import connections" + }, { "category": "KX", "command": "kdb.refreshServerObjects", @@ -701,6 +711,16 @@ "command": "kdb.resultsPanel.export.csv", "when": "view == kdb-results", "group": "resultsPanel" + }, + { + "command": "kdb.connections.export", + "when": "view == kdb-servers", + "group": "inline" + }, + { + "command": "kdb.connections.import", + "when": "view == kdb-servers", + "group": "inline" } ], "view/item/context": [ diff --git a/src/extension.ts b/src/extension.ts index 21f973a2..1453facf 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -267,6 +267,12 @@ export async function activate(context: ExtensionContext) { await disconnect(connLabel); }, ), + commands.registerCommand("kdb.connections.export", () => { + window.showInformationMessage("Export Connections command executed"); + }), + commands.registerCommand("kdb.connections.import", () => { + window.showInformationMessage("Import Connections command executed"); + }), commands.registerCommand( "kdb.open.meta", async (viewItem: InsightsMetaNode | MetaObjectPayloadNode) => { From 63a4973ea686815478601087faedfde668c1a37c Mon Sep 17 00:00:00 2001 From: Philip Carneiro Date: Wed, 4 Sep 2024 09:32:29 +0100 Subject: [PATCH 2/5] centralized all connections in one place --- src/commands/dataSourceCommand.ts | 2 +- src/commands/installTools.ts | 2 +- src/commands/serverCommand.ts | 35 +++++++++++++++++-- src/extension.ts | 8 +++-- .../{server.ts => connectionsModels.ts} | 21 +++++++++++ src/models/queryHistory.ts | 2 +- src/services/connectionManagerService.ts | 7 ++-- .../exportConnContentProvider.ts} | 26 +++++++++----- src/services/kdbTreeProvider.ts | 8 +++-- src/utils/core.ts | 8 +++-- src/utils/executionConsole.ts | 2 +- src/utils/queryUtils.ts | 2 +- .../components/kdbNewConnectionView.ts | 7 ++-- test/suite/commands.test.ts | 7 ++-- test/suite/services.test.ts | 7 ++-- test/suite/utils.test.ts | 7 ++-- test/suite/webview.test.ts | 3 +- 17 files changed, 120 insertions(+), 34 deletions(-) rename src/models/{server.ts => connectionsModels.ts} (71%) rename src/{models/insights.ts => services/exportConnContentProvider.ts} (55%) diff --git a/src/commands/dataSourceCommand.ts b/src/commands/dataSourceCommand.ts index f8461ebb..b5fec5be 100644 --- a/src/commands/dataSourceCommand.ts +++ b/src/commands/dataSourceCommand.ts @@ -40,12 +40,12 @@ import { writeQueryResultsToConsole, writeQueryResultsToView, } from "./serverCommand"; -import { ServerType } from "../models/server"; import { Telemetry } from "../utils/telemetryClient"; import { LocalConnection } from "../classes/localConnection"; import { ConnectionManagementService } from "../services/connectionManagerService"; import { InsightsConnection } from "../classes/insightsConnection"; import { kdbOutputLog, offerConnectAction } from "../utils/core"; +import { ServerType } from "../models/connectionsModels"; export async function addDataSource(): Promise { const kdbDataSourcesFolderPath = createKdbDataSourcesFolder(); diff --git a/src/commands/installTools.ts b/src/commands/installTools.ts index 799be259..a7befef7 100644 --- a/src/commands/installTools.ts +++ b/src/commands/installTools.ts @@ -43,7 +43,6 @@ import { onboardingInput, onboardingWorkflow, } from "../models/items/onboarding"; -import { Server } from "../models/server"; import { KdbNode } from "../services/kdbTreeProvider"; import { addLocalConnectionContexts, @@ -64,6 +63,7 @@ import { executeCommand } from "../utils/cpUtils"; import { openUrl } from "../utils/openUrl"; import { Telemetry } from "../utils/telemetryClient"; import { validateServerPort } from "../validators/kdbValidator"; +import { Server } from "../models/connectionsModels"; export async function installTools(): Promise { let file: Uri[] | undefined; diff --git a/src/commands/serverCommand.ts b/src/commands/serverCommand.ts index 76c740b5..bccfe8e5 100644 --- a/src/commands/serverCommand.ts +++ b/src/commands/serverCommand.ts @@ -26,10 +26,8 @@ import { import { ext } from "../extensionVariables"; import { DataSourceFiles } from "../models/dataSource"; import { ExecutionTypes } from "../models/execution"; -import { InsightDetails, Insights } from "../models/insights"; import { queryConstants } from "../models/queryResult"; import { ScratchpadResult } from "../models/scratchpadResult"; -import { Server, ServerDetails, ServerType } from "../models/server"; import { ServerObject } from "../models/serverObject"; import { DataSourcesPanel } from "../panels/datasource"; import { @@ -69,6 +67,14 @@ import { ConnectionManagementService } from "../services/connectionManagerServic import { InsightsConnection } from "../classes/insightsConnection"; import { MetaContentProvider } from "../services/metaContentProvider"; import { handleLabelsConnMap, removeConnFromLabels } from "../utils/connLabel"; +import { ExportConnectionContentProvider } from "../services/exportConnContentProvider"; +import { + InsightDetails, + Insights, + Server, + ServerDetails, + ServerType, +} from "../models/connectionsModels"; export async function addNewConnection(): Promise { NewConnectionPannel.close(); @@ -875,6 +881,31 @@ export async function openMeta(node: MetaObjectPayloadNode | InsightsMetaNode) { } } +export async function exportConnection(connLabel?: string) { + const exportConnProvider = new ExportConnectionContentProvider(); + workspace.registerTextDocumentContentProvider( + "Export Connection", + exportConnProvider, + ); + const connMngService = new ConnectionManagementService(); + const doc = connMngService.exportConnection(connLabel); + if (doc && doc !== "") { + const formattedDoc = JSON.stringify(JSON.parse(doc), null, 2); + const uri = Uri.parse(`exported-connections.json`); + exportConnProvider.update(uri, formattedDoc); + const document = await workspace.openTextDocument(uri); + await window.showTextDocument(document, { + preview: false, + viewColumn: ViewColumn.One, + }); + } else { + kdbOutputLog( + "[EXPORT CONNECTIONS] No connections found to be exported", + "ERROR", + ); + } +} + export function writeQueryResultsToConsole( result: string | string[], query: string, diff --git a/src/extension.ts b/src/extension.ts index 1453facf..c9171d8e 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -59,9 +59,7 @@ import { import { showInstallationDetails } from "./commands/walkthroughCommand"; import { ext } from "./extensionVariables"; import { ExecutionTypes } from "./models/execution"; -import { InsightDetails, Insights } from "./models/insights"; import { QueryResult } from "./models/queryResult"; -import { Server, ServerDetails } from "./models/server"; import { InsightsMetaNode, InsightsNode, @@ -114,6 +112,12 @@ import { setLabelColor, } from "./utils/connLabel"; import { activateTextDocument } from "./utils/workspace"; +import { + InsightDetails, + Insights, + Server, + ServerDetails, +} from "./models/connectionsModels"; let client: LanguageClient; diff --git a/src/models/server.ts b/src/models/connectionsModels.ts similarity index 71% rename from src/models/server.ts rename to src/models/connectionsModels.ts index af400b55..3f0f0ee6 100644 --- a/src/models/server.ts +++ b/src/models/connectionsModels.ts @@ -11,6 +11,8 @@ * specific language governing permissions and limitations under the License. */ +//TODO: start to migrate all connections models to here + export enum ServerType { INSIGHTS, KDB, @@ -31,3 +33,22 @@ export interface ServerDetails { export interface Server { [name: string]: ServerDetails; } + +export interface InsightDetails { + alias: string; + server: string; + auth: boolean; + realm?: string; + insecure?: boolean; +} + +export interface Insights { + [name: string]: InsightDetails; +} + +export interface ExportedConnections { + connections: { + Insights: InsightDetails[]; + KDB: ServerDetails[]; + }; +} diff --git a/src/models/queryHistory.ts b/src/models/queryHistory.ts index 1fcb9be7..4071150f 100644 --- a/src/models/queryHistory.ts +++ b/src/models/queryHistory.ts @@ -11,8 +11,8 @@ * specific language governing permissions and limitations under the License. */ +import { ServerType } from "./connectionsModels"; import { DataSourceFiles, DataSourceTypes } from "./dataSource"; -import { ServerType } from "./server"; export interface QueryHistory { executorName: string; diff --git a/src/services/connectionManagerService.ts b/src/services/connectionManagerService.ts index 1a509bad..08214886 100644 --- a/src/services/connectionManagerService.ts +++ b/src/services/connectionManagerService.ts @@ -28,11 +28,10 @@ import { updateInsights, updateServers, } from "../utils/core"; -import { Insights } from "../models/insights"; -import { Server } from "../models/server"; import { refreshDataSourcesPanel } from "../utils/dataSource"; import { MetaInfoType } from "../models/meta"; import { retrieveConnLabelsNames } from "../utils/connLabel"; +import { Insights, Server } from "../models/connectionsModels"; export class ConnectionManagementService { public retrieveConnection( @@ -403,4 +402,8 @@ export class ConnectionManagementService { return connection.returnMetaObject(metaType); } + + public exportConnection(connLabel?: string): string { + return ""; + } } diff --git a/src/models/insights.ts b/src/services/exportConnContentProvider.ts similarity index 55% rename from src/models/insights.ts rename to src/services/exportConnContentProvider.ts index b9446e4d..1160d53d 100644 --- a/src/models/insights.ts +++ b/src/services/exportConnContentProvider.ts @@ -11,14 +11,22 @@ * specific language governing permissions and limitations under the License. */ -export interface InsightDetails { - alias: string; - server: string; - auth: boolean; - realm?: string; - insecure?: boolean; -} +import * as vscode from "vscode"; + +export class ExportConnectionContentProvider + implements vscode.TextDocumentContentProvider +{ + private _onDidChange = new vscode.EventEmitter(); + public readonly onDidChange = this._onDidChange.event; + + private content: string = ""; + + public update(uri: vscode.Uri, content: string) { + this.content = content; + this._onDidChange.fire(uri); + } -export interface Insights { - [name: string]: InsightDetails; + provideTextDocumentContent(uri: vscode.Uri): string { + return this.content; + } } diff --git a/src/services/kdbTreeProvider.ts b/src/services/kdbTreeProvider.ts index 55b20d2a..5ba38f3c 100644 --- a/src/services/kdbTreeProvider.ts +++ b/src/services/kdbTreeProvider.ts @@ -22,8 +22,6 @@ import { commands, } from "vscode"; import { ext } from "../extensionVariables"; -import { InsightDetails, Insights } from "../models/insights"; -import { Server, ServerDetails } from "../models/server"; import { loadDictionaries, loadFunctions, @@ -49,6 +47,12 @@ import { retrieveConnLabelsNames, } from "../utils/connLabel"; import { Labels } from "../models/labels"; +import { + InsightDetails, + Insights, + Server, + ServerDetails, +} from "../models/connectionsModels"; export class KdbTreeProvider implements TreeDataProvider { private _onDidChangeTreeData: EventEmitter< diff --git a/src/utils/core.ts b/src/utils/core.ts index 90cef80d..18a539a4 100644 --- a/src/utils/core.ts +++ b/src/utils/core.ts @@ -22,12 +22,16 @@ import * as semver from "semver"; import { commands, ConfigurationTarget, Uri, window, workspace } from "vscode"; import { installTools } from "../commands/installTools"; import { ext } from "../extensionVariables"; -import { InsightDetails, Insights } from "../models/insights"; import { QueryResult } from "../models/queryResult"; -import { Server, ServerDetails } from "../models/server"; import { tryExecuteCommand } from "./cpUtils"; import { showRegistrationNotification } from "./registration"; import { Telemetry } from "./telemetryClient"; +import { + InsightDetails, + Insights, + Server, + ServerDetails, +} from "../models/connectionsModels"; export function log(childProcess: ChildProcess): void { kdbOutputLog(`Process ${childProcess.pid} started`, "INFO"); diff --git a/src/utils/executionConsole.ts b/src/utils/executionConsole.ts index a6bc6d0f..63d83120 100644 --- a/src/utils/executionConsole.ts +++ b/src/utils/executionConsole.ts @@ -13,7 +13,6 @@ import { OutputChannel, commands, window } from "vscode"; import { ext } from "../extensionVariables"; -import { ServerType } from "../models/server"; import { getHideDetailedConsoleQueryOutput, setOutputWordWrapper, @@ -23,6 +22,7 @@ import { checkIfIsDatasource, convertRowsToConsole, } from "./queryUtils"; +import { ServerType } from "../models/connectionsModels"; export class ExecutionConsole { public static current: ExecutionConsole | undefined; diff --git a/src/utils/queryUtils.ts b/src/utils/queryUtils.ts index ac60a394..c572e327 100644 --- a/src/utils/queryUtils.ts +++ b/src/utils/queryUtils.ts @@ -16,12 +16,12 @@ import { join } from "path"; import { ext } from "../extensionVariables"; import { DCDS, deserialize, isCompressed, uncompress } from "../ipc/c"; import { Parse } from "../ipc/parse.qlist"; -import { ServerType } from "../models/server"; import { DDateClass, DDateTimeClass, DTimestampClass } from "../ipc/cClasses"; import { TypeBase } from "../ipc/typeBase"; import { DataSourceFiles, DataSourceTypes } from "../models/dataSource"; import { QueryHistory } from "../models/queryHistory"; import { kdbOutputLog } from "./core"; +import { ServerType } from "../models/connectionsModels"; export function sanitizeQuery(query: string): string { if (query[0] === "`") { diff --git a/src/webview/components/kdbNewConnectionView.ts b/src/webview/components/kdbNewConnectionView.ts index c488fb49..2097f2fd 100644 --- a/src/webview/components/kdbNewConnectionView.ts +++ b/src/webview/components/kdbNewConnectionView.ts @@ -13,13 +13,16 @@ import { LitElement, html } from "lit"; import { customElement } from "lit/decorators.js"; -import { ServerDetails, ServerType } from "../../models/server"; -import { InsightDetails } from "../../models/insights"; import { kdbStyles, newConnectionStyles, vscodeStyles } from "./styles"; import { EditConnectionMessage } from "../../models/messages"; import { repeat } from "lit/directives/repeat.js"; import { LabelColors, Labels } from "../../models/labels"; +import { + InsightDetails, + ServerDetails, + ServerType, +} from "../../models/connectionsModels"; @customElement("kdb-new-connection-view") export class KdbNewConnectionView extends LitElement { diff --git a/test/suite/commands.test.ts b/test/suite/commands.test.ts index 548de197..5597adb0 100644 --- a/test/suite/commands.test.ts +++ b/test/suite/commands.test.ts @@ -27,7 +27,6 @@ import { createDefaultDataSourceFile, } from "../../src/models/dataSource"; import { ExecutionTypes } from "../../src/models/execution"; -import { InsightDetails } from "../../src/models/insights"; import { ScratchpadResult } from "../../src/models/scratchpadResult"; import { InsightsNode, @@ -41,7 +40,6 @@ import * as dataSourceUtils from "../../src/utils/dataSource"; import { ExecutionConsole } from "../../src/utils/executionConsole"; import * as queryUtils from "../../src/utils/queryUtils"; import { QueryHistory } from "../../src/models/queryHistory"; -import { ServerDetails, ServerType } from "../../src/models/server"; import { NewConnectionPannel } from "../../src/panels/newConnection"; import { MAX_STR_LEN } from "../../src/validators/kdbValidator"; import { LocalConnection } from "../../src/classes/localConnection"; @@ -53,6 +51,11 @@ import { WorkspaceTreeProvider } from "../../src/services/workspaceTreeProvider" import { GetDataError } from "../../src/models/data"; import * as clientCommand from "../../src/commands/clientCommands"; import { LanguageClient } from "vscode-languageclient/node"; +import { + InsightDetails, + ServerDetails, + ServerType, +} from "../../src/models/connectionsModels"; describe("dataSourceCommand", () => { afterEach(() => { diff --git a/test/suite/services.test.ts b/test/suite/services.test.ts index 93816d5e..97908c6f 100644 --- a/test/suite/services.test.ts +++ b/test/suite/services.test.ts @@ -25,9 +25,7 @@ import { workspace, } from "vscode"; import { ext } from "../../src/extensionVariables"; -import { Insights } from "../../src/models/insights"; import { QueryHistory } from "../../src/models/queryHistory"; -import { Server, ServerType } from "../../src/models/server"; import { getCurrentToken, refreshToken, @@ -65,6 +63,11 @@ import { MetaInfoType, MetaObject } from "../../src/models/meta"; import { CompletionProvider } from "../../src/services/completionProvider"; import { MetaContentProvider } from "../../src/services/metaContentProvider"; import { ConnectionLabel, Labels } from "../../src/models/labels"; +import { + Insights, + Server, + ServerType, +} from "../../src/models/connectionsModels"; // eslint-disable-next-line @typescript-eslint/no-var-requires const codeFlow = require("../../src/services/kdbInsights/codeFlowLogin"); diff --git a/test/suite/utils.test.ts b/test/suite/utils.test.ts index 26c339eb..c8044f71 100644 --- a/test/suite/utils.test.ts +++ b/test/suite/utils.test.ts @@ -20,7 +20,6 @@ import { ext } from "../../src/extensionVariables"; import * as QTable from "../../src/ipc/QTable"; import { CancellationEvent } from "../../src/models/cancellationEvent"; import { QueryResultType } from "../../src/models/queryResult"; -import { ServerDetails, ServerType } from "../../src/models/server"; import { InsightsNode, KdbNode } from "../../src/services/kdbTreeProvider"; import { QueryHistoryProvider } from "../../src/services/queryHistoryProvider"; import * as coreUtils from "../../src/utils/core"; @@ -49,9 +48,13 @@ import { DTimestampClass, } from "../../src/ipc/cClasses"; import { DataSourceTypes } from "../../src/models/dataSource"; -import { InsightDetails } from "../../src/models/insights"; import { LocalConnection } from "../../src/classes/localConnection"; import { Labels } from "../../src/models/labels"; +import { + InsightDetails, + ServerDetails, + ServerType, +} from "../../src/models/connectionsModels"; interface ITestItem extends vscode.QuickPickItem { id: number; diff --git a/test/suite/webview.test.ts b/test/suite/webview.test.ts index dc9f9a3a..b9f2301c 100644 --- a/test/suite/webview.test.ts +++ b/test/suite/webview.test.ts @@ -18,9 +18,7 @@ import * as assert from "assert"; import * as sinon from "sinon"; import { KdbDataSourceView } from "../../src/webview/components/kdbDataSourceView"; import { KdbNewConnectionView } from "../../src/webview/components/kdbNewConnectionView"; -import { ServerType } from "../../src/models/server"; -import { InsightDetails } from "../../src/models/insights"; import { DataSourceCommand, DataSourceMessage2, @@ -37,6 +35,7 @@ import { import { MetaObjectPayload } from "../../src/models/meta"; import { html, TemplateResult } from "lit"; import { ext } from "../../src/extensionVariables"; +import { InsightDetails, ServerType } from "../../src/models/connectionsModels"; describe("KdbDataSourceView", () => { let view: KdbDataSourceView; From d34718f973d695916cca6498a4310d0745eddc70 Mon Sep 17 00:00:00 2001 From: Philip Carneiro Date: Wed, 4 Sep 2024 09:52:52 +0100 Subject: [PATCH 3/5] open json from exported connections --- package.json | 4 +-- src/commands/serverCommand.ts | 6 ++-- src/extension.ts | 5 ++-- src/services/connectionManagerService.ts | 36 ++++++++++++++++++++++-- 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 66b63a16..ec56e9da 100644 --- a/package.json +++ b/package.json @@ -202,7 +202,7 @@ "commands": [ { "category": "KX", - "command": "kdb.connections.export", + "command": "kdb.connections.export.all", "title": "Export connections" }, { @@ -713,7 +713,7 @@ "group": "resultsPanel" }, { - "command": "kdb.connections.export", + "command": "kdb.connections.export.all", "when": "view == kdb-servers", "group": "inline" }, diff --git a/src/commands/serverCommand.ts b/src/commands/serverCommand.ts index bccfe8e5..d7bc7c91 100644 --- a/src/commands/serverCommand.ts +++ b/src/commands/serverCommand.ts @@ -881,17 +881,17 @@ export async function openMeta(node: MetaObjectPayloadNode | InsightsMetaNode) { } } -export async function exportConnection(connLabel?: string) { +export async function exportConnections(connLabel?: string) { const exportConnProvider = new ExportConnectionContentProvider(); workspace.registerTextDocumentContentProvider( - "Export Connection", + "connections", exportConnProvider, ); const connMngService = new ConnectionManagementService(); const doc = connMngService.exportConnection(connLabel); if (doc && doc !== "") { const formattedDoc = JSON.stringify(JSON.parse(doc), null, 2); - const uri = Uri.parse(`exported-connections.json`); + const uri = Uri.parse(`connections:exported-connections.json`); exportConnProvider.update(uri, formattedDoc); const document = await workspace.openTextDocument(uri); await window.showTextDocument(document, { diff --git a/src/extension.ts b/src/extension.ts index c9171d8e..4a1aaf5d 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -51,6 +51,7 @@ import { editInsightsConnection, editKdbConnection, enableTLS, + exportConnections, openMeta, refreshGetMeta, removeConnection, @@ -271,8 +272,8 @@ export async function activate(context: ExtensionContext) { await disconnect(connLabel); }, ), - commands.registerCommand("kdb.connections.export", () => { - window.showInformationMessage("Export Connections command executed"); + commands.registerCommand("kdb.connections.export.all", () => { + exportConnections(); }), commands.registerCommand("kdb.connections.import", () => { window.showInformationMessage("Import Connections command executed"); diff --git a/src/services/connectionManagerService.ts b/src/services/connectionManagerService.ts index 08214886..666da851 100644 --- a/src/services/connectionManagerService.ts +++ b/src/services/connectionManagerService.ts @@ -31,7 +31,11 @@ import { import { refreshDataSourcesPanel } from "../utils/dataSource"; import { MetaInfoType } from "../models/meta"; import { retrieveConnLabelsNames } from "../utils/connLabel"; -import { Insights, Server } from "../models/connectionsModels"; +import { + ExportedConnections, + Insights, + Server, +} from "../models/connectionsModels"; export class ConnectionManagementService { public retrieveConnection( @@ -404,6 +408,34 @@ export class ConnectionManagementService { } public exportConnection(connLabel?: string): string { - return ""; + const exportedContent: ExportedConnections = { + connections: { + Insights: [], + KDB: [], + }, + }; + if (connLabel) { + const connection = this.retrieveConnection(connLabel); + if (!connection) { + return ""; + } + if (connection instanceof KdbNode) { + exportedContent.connections.KDB.push(connection.details); + } else { + exportedContent.connections.Insights.push(connection.details); + } + } else { + ext.connectionsList.forEach((connection) => { + if (connection instanceof KdbNode) { + exportedContent.connections.KDB.push(connection.details); + } else { + exportedContent.connections.Insights.push(connection.details); + } + }); + } + return exportedContent.connections.Insights.length === 0 && + exportedContent.connections.KDB.length === 0 + ? "" + : JSON.stringify(exportedContent, null, 2); } } From e1d6c33602a721525966e09e524d432db9b70441 Mon Sep 17 00:00:00 2001 From: Philip Carneiro Date: Thu, 5 Sep 2024 07:45:48 +0100 Subject: [PATCH 4/5] add export method to the extension --- package.json | 10 ++++++ src/commands/serverCommand.ts | 39 ++++++++++++++++------- src/extension.ts | 6 ++++ src/services/connectionManagerService.ts | 2 ++ src/services/exportConnContentProvider.ts | 32 ------------------- src/utils/core.ts | 11 +++---- 6 files changed, 50 insertions(+), 50 deletions(-) delete mode 100644 src/services/exportConnContentProvider.ts diff --git a/package.json b/package.json index ec56e9da..3b44d2f6 100644 --- a/package.json +++ b/package.json @@ -205,6 +205,11 @@ "command": "kdb.connections.export.all", "title": "Export connections" }, + { + "category": "KX", + "command": "kdb.connections.export.single", + "title": "Export connection" + }, { "category": "KX", "command": "kdb.connections.import", @@ -769,6 +774,11 @@ "when": "view == kdb-servers && viewItem in kdb.rootNodes", "group": "connection@5" }, + { + "command": "kdb.connections.export.single", + "when": "view == kdb-servers && (viewItem in kdb.rootNodes || viewItem in kdb.insightsNodes)", + "group": "connection@6" + }, { "command": "kdb.startLocalProcess", "when": "view == kdb-servers && viewItem in kdb.local && viewItem not in kdb.running && viewItem in kdb.rootNodes", diff --git a/src/commands/serverCommand.ts b/src/commands/serverCommand.ts index d7bc7c91..0a6088c3 100644 --- a/src/commands/serverCommand.ts +++ b/src/commands/serverCommand.ts @@ -67,7 +67,6 @@ import { ConnectionManagementService } from "../services/connectionManagerServic import { InsightsConnection } from "../classes/insightsConnection"; import { MetaContentProvider } from "../services/metaContentProvider"; import { handleLabelsConnMap, removeConnFromLabels } from "../utils/connLabel"; -import { ExportConnectionContentProvider } from "../services/exportConnContentProvider"; import { InsightDetails, Insights, @@ -75,6 +74,7 @@ import { ServerDetails, ServerType, } from "../models/connectionsModels"; +import * as fs from "fs"; export async function addNewConnection(): Promise { NewConnectionPannel.close(); @@ -882,22 +882,37 @@ export async function openMeta(node: MetaObjectPayloadNode | InsightsMetaNode) { } export async function exportConnections(connLabel?: string) { - const exportConnProvider = new ExportConnectionContentProvider(); - workspace.registerTextDocumentContentProvider( - "connections", - exportConnProvider, - ); const connMngService = new ConnectionManagementService(); const doc = connMngService.exportConnection(connLabel); if (doc && doc !== "") { const formattedDoc = JSON.stringify(JSON.parse(doc), null, 2); - const uri = Uri.parse(`connections:exported-connections.json`); - exportConnProvider.update(uri, formattedDoc); - const document = await workspace.openTextDocument(uri); - await window.showTextDocument(document, { - preview: false, - viewColumn: ViewColumn.One, + const uri = await window.showSaveDialog({ + saveLabel: "Save Exported Connections", + filters: { + "JSON Files": ["json"], + "All Files": ["*"], + }, }); + if (uri) { + fs.writeFile(uri.fsPath, formattedDoc, (err) => { + if (err) { + kdbOutputLog( + `[EXPORT CONNECTIONS] Error saving file: ${err.message}`, + "ERROR", + ); + window.showErrorMessage(`Error saving file: ${err.message}`); + } else { + workspace.openTextDocument(uri).then((document) => { + window.showTextDocument(document, { preview: false }); + }); + } + }); + } else { + kdbOutputLog( + "[EXPORT CONNECTIONS] Save operation was cancelled by the user", + "INFO", + ); + } } else { kdbOutputLog( "[EXPORT CONNECTIONS] No connections found to be exported", diff --git a/src/extension.ts b/src/extension.ts index 4a1aaf5d..ceaa0315 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -275,6 +275,12 @@ export async function activate(context: ExtensionContext) { commands.registerCommand("kdb.connections.export.all", () => { exportConnections(); }), + commands.registerCommand( + "kdb.connections.export.single", + async (viewItem: KdbNode | InsightsNode) => { + exportConnections(viewItem.label); + }, + ), commands.registerCommand("kdb.connections.import", () => { window.showInformationMessage("Import Connections command executed"); }), diff --git a/src/services/connectionManagerService.ts b/src/services/connectionManagerService.ts index 666da851..623c5dec 100644 --- a/src/services/connectionManagerService.ts +++ b/src/services/connectionManagerService.ts @@ -420,6 +420,7 @@ export class ConnectionManagementService { return ""; } if (connection instanceof KdbNode) { + connection.details.auth = false; exportedContent.connections.KDB.push(connection.details); } else { exportedContent.connections.Insights.push(connection.details); @@ -427,6 +428,7 @@ export class ConnectionManagementService { } else { ext.connectionsList.forEach((connection) => { if (connection instanceof KdbNode) { + connection.details.auth = false; exportedContent.connections.KDB.push(connection.details); } else { exportedContent.connections.Insights.push(connection.details); diff --git a/src/services/exportConnContentProvider.ts b/src/services/exportConnContentProvider.ts deleted file mode 100644 index 1160d53d..00000000 --- a/src/services/exportConnContentProvider.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 1998-2023 Kx Systems Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -import * as vscode from "vscode"; - -export class ExportConnectionContentProvider - implements vscode.TextDocumentContentProvider -{ - private _onDidChange = new vscode.EventEmitter(); - public readonly onDidChange = this._onDidChange.event; - - private content: string = ""; - - public update(uri: vscode.Uri, content: string) { - this.content = content; - this._onDidChange.fire(uri); - } - - provideTextDocumentContent(uri: vscode.Uri): string { - return this.content; - } -} diff --git a/src/utils/core.ts b/src/utils/core.ts index 18a539a4..31a6d94b 100644 --- a/src/utils/core.ts +++ b/src/utils/core.ts @@ -316,6 +316,11 @@ export function kdbOutputLog(message: string, type: string): void { const dateNow = new Date().toLocaleDateString(); const timeNow = new Date().toLocaleTimeString(); ext.outputChannel.appendLine(`[${dateNow} ${timeNow}] [${type}] ${message}`); + if (type === "ERROR") { + window.showErrorMessage( + `Error occured, check kdb output channel for details.`, + ); + } } export function tokenUndefinedError(connLabel: string): void { @@ -323,9 +328,6 @@ export function tokenUndefinedError(connLabel: string): void { `Error retrieving access token for Insights connection named: ${connLabel}`, "ERROR", ); - window.showErrorMessage( - `Error retrieving access token for Insights connection named: ${connLabel}`, - ); } export function invalidUsernameJWT(connLabel: string): void { @@ -333,9 +335,6 @@ export function invalidUsernameJWT(connLabel: string): void { `JWT did not contain a valid preferred username for Insights connection: ${connLabel}`, "ERROR", ); - window.showErrorMessage( - `JWT did not contain a valid preferred username for Insights connection: ${connLabel}`, - ); } /* istanbul ignore next */ From 16339d9c2f6d83c5119fcd7d009258579bc062ad Mon Sep 17 00:00:00 2001 From: Philip Carneiro Date: Thu, 5 Sep 2024 10:13:25 +0100 Subject: [PATCH 5/5] add tests --- test/suite/commands.test.ts | 53 ++++++++++++++++++++++++ test/suite/services.test.ts | 80 +++++++++++++++++++++++++++++++++++++ test/suite/utils.test.ts | 12 ++++++ 3 files changed, 145 insertions(+) diff --git a/test/suite/commands.test.ts b/test/suite/commands.test.ts index 5597adb0..4c3e0ffd 100644 --- a/test/suite/commands.test.ts +++ b/test/suite/commands.test.ts @@ -11,6 +11,7 @@ * 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"; @@ -1890,6 +1891,58 @@ describe("serverCommand", () => { sinon.assert.notCalled(vscode.window.showTextDocument as sinon.SinonSpy); }); }); + + describe("exportConnections", () => { + let sandbox: sinon.SinonSandbox; + let kdbOutputLogStub: sinon.SinonStub; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + kdbOutputLogStub = sinon.stub(coreUtils, "kdbOutputLog"); + }); + + afterEach(() => { + sandbox.restore(); + sinon.restore(); + mock.restore(); + }); + + it("should log an error when no connections are found", async () => { + const exportConnectionStub = sandbox + .stub(ConnectionManagementService.prototype, "exportConnection") + .returns(""); + + await serverCommand.exportConnections(); + + sinon.assert.calledOnce(kdbOutputLogStub); + sinon.assert.calledWith( + kdbOutputLogStub, + "[EXPORT CONNECTIONS] No connections found to be exported", + "ERROR", + ); + exportConnectionStub.restore(); + }); + + it("should log info when save operation is cancelled by the user", async () => { + const exportConnectionStub = sandbox + .stub(ConnectionManagementService.prototype, "exportConnection") + .returns("{}"); + const showSaveDialogStub = sandbox + .stub(vscode.window, "showSaveDialog") + .resolves(undefined); + + await serverCommand.exportConnections(); + + sinon.assert.calledOnce(kdbOutputLogStub); + sinon.assert.calledWith( + kdbOutputLogStub, + "[EXPORT CONNECTIONS] Save operation was cancelled by the user", + "INFO", + ); + exportConnectionStub.restore(); + showSaveDialogStub.restore(); + }); + }); }); describe("walkthroughCommand", () => { diff --git a/test/suite/services.test.ts b/test/suite/services.test.ts index 97908c6f..de72d81d 100644 --- a/test/suite/services.test.ts +++ b/test/suite/services.test.ts @@ -1396,6 +1396,86 @@ describe("connectionManagerService", () => { ); }); }); + + describe("ConnectionManagementService", () => { + let retrieveConnectionStub: sinon.SinonStub; + let connectionsListStub: sinon.SinonStub; + + beforeEach(() => { + retrieveConnectionStub = sinon.stub( + connectionManagerService, + "retrieveConnection", + ); + connectionsListStub = sinon.stub(ext, "connectionsList").value([]); + }); + + afterEach(() => { + sinon.restore(); + }); + + it("should return empty string if connLabel is provided and connection is not found", () => { + retrieveConnectionStub.withArgs("nonExistentLabel").returns(null); + + const result = + connectionManagerService.exportConnection("nonExistentLabel"); + + assert.strictEqual(result, ""); + }); + + it("should export KDB connection when connLabel is provided and connection is an instance of KdbNode", () => { + kdbNode.details.auth = true; + retrieveConnectionStub.withArgs("kdbLabel").returns(kdbNode); + + const result = connectionManagerService.exportConnection("kdbLabel"); + + const expectedOutput = { + connections: { + Insights: [], + KDB: [kdbNode.details], + }, + }; + + assert.strictEqual(result, JSON.stringify(expectedOutput, null, 2)); + }); + + it("should export Insights connection when connLabel is provided and connection is not an instance of KdbNode", () => { + retrieveConnectionStub.withArgs("insightsLabel").returns(insightNode); + + const result = connectionManagerService.exportConnection("insightsLabel"); + + const expectedOutput = { + connections: { + Insights: [insightNode.details], + KDB: [], + }, + }; + + assert.strictEqual(result, JSON.stringify(expectedOutput, null, 2)); + }); + + it("should return empty string if connLabel is not provided and connectionsList is empty", () => { + connectionsListStub.value([]); + + const result = connectionManagerService.exportConnection(); + + assert.strictEqual(result, ""); + }); + + it("should export all connections when connLabel is not provided and connectionsList contains instances of KdbNode and other connections", () => { + connectionsListStub.value([kdbNode, insightNode]); + + const result = connectionManagerService.exportConnection(); + + const expectedOutput = { + connections: { + Insights: [insightNode.details], + KDB: [kdbNode.details], + }, + }; + + assert.strictEqual(result, JSON.stringify(expectedOutput, null, 2)); + }); + }); }); describe("dataSourceEditorProvider", () => { diff --git a/test/suite/utils.test.ts b/test/suite/utils.test.ts index c8044f71..c6d9b117 100644 --- a/test/suite/utils.test.ts +++ b/test/suite/utils.test.ts @@ -312,6 +312,18 @@ describe("Utils", () => { appendLineSpy.calledWithMatch(type); }); + it("kdbOutputLog should log message with date and ERROR type", () => { + const message = "test message"; + const type = "ERROR"; + + coreUtils.kdbOutputLog(message, type); + + appendLineSpy.calledOnce; + showErrorMessageSpy.calledOnce; + appendLineSpy.calledWithMatch(message); + appendLineSpy.calledWithMatch(type); + }); + it("tokenUndefinedError should log and show error message", () => { const connLabel = "test connection";