From e5584415b941b80ffb0a669719804a343210a452 Mon Sep 17 00:00:00 2001 From: ecmel Date: Mon, 23 Jun 2025 10:15:24 +0300 Subject: [PATCH 01/10] unified progress, log and notifications --- src/classes/insightsConnection.ts | 353 ++++++++++------------- src/classes/localConnection.ts | 66 +++-- src/commands/buildToolsCommand.ts | 62 ++-- src/commands/dataSourceCommand.ts | 68 +++-- src/commands/installTools.ts | 335 +++++++++++---------- src/commands/serverCommand.ts | 210 +++++++------- src/commands/walkthroughCommand.ts | 6 +- src/commands/workspaceCommand.ts | 24 +- src/extension.ts | 16 +- src/panels/newConnection.ts | 4 +- src/services/connectionManagerService.ts | 88 +++--- src/services/dataSourceEditorProvider.ts | 33 +-- src/services/notebookController.ts | 39 ++- src/services/resultsPanelProvider.ts | 12 +- src/utils/connLabel.ts | 10 +- src/utils/core.ts | 173 ++++++----- src/utils/cpUtils.ts | 15 +- src/utils/dataSource.ts | 32 +- src/utils/execution.ts | 13 +- src/utils/executionConsole.ts | 6 +- src/utils/feedbackSurveyUtils.ts | 5 +- src/utils/loggers.ts | 31 ++ src/utils/notifications.ts | 131 +++++++++ src/utils/queryUtils.ts | 7 +- src/utils/registration.ts | 23 +- src/utils/shell.ts | 12 +- test/suite/commands.test.ts | 28 +- test/suite/panels.test.ts | 6 +- test/suite/services.test.ts | 14 +- test/suite/utils.test.ts | 29 +- 30 files changed, 1040 insertions(+), 811 deletions(-) create mode 100644 src/utils/loggers.ts create mode 100644 src/utils/notifications.ts diff --git a/src/classes/insightsConnection.ts b/src/classes/insightsConnection.ts index 3706ff9d..1880bc77 100644 --- a/src/classes/insightsConnection.ts +++ b/src/classes/insightsConnection.ts @@ -14,7 +14,6 @@ import axios, { AxiosRequestConfig } from "axios"; import { jwtDecode } from "jwt-decode"; import * as url from "url"; -import { ProgressLocation, window } from "vscode"; import { ext } from "../extensionVariables"; import { isCompressed, uncompress } from "../ipc/c"; @@ -39,10 +38,10 @@ import { InsightsNode } from "../services/kdbTreeProvider"; import { isBaseVersionGreaterOrEqual, invalidUsernameJWT, - kdbOutputLog, tokenUndefinedError, } from "../utils/core"; import { convertTimeToTimestamp } from "../utils/dataSource"; +import { MessageKind, Runner, showMessage } from "../utils/notifications"; import { generateQSqlBody, handleScratchpadTableRes, @@ -51,6 +50,8 @@ import { import { Telemetry } from "../utils/telemetryClient"; import { retrieveUDAtoCreateReqBody } from "../utils/uda"; +const logger = "insightsConnection"; + const customHeadersOctet = { Accept: "application/octet-stream", "Content-Type": "application/json", @@ -85,7 +86,7 @@ export class InsightsConnection { await this.getConfig(); await this.getMeta(); await this.getApiConfig(); - await this.getScratchpadQuery("", undefined, false, true); + this.getScratchpadQuery("", undefined, false, true, false); } }); return this.connected; @@ -153,9 +154,10 @@ export class InsightsConnection { public returnMetaObject(metaType: MetaInfoType): string { if (!this.meta) { - kdbOutputLog( + showMessage( `Meta data is undefined for connection ${this.connLabel}`, - "ERROR", + MessageKind.ERROR, + { logger }, ); return ""; } @@ -182,7 +184,9 @@ export class InsightsConnection { objectToReturn = this.meta.payload.rc; break; default: - kdbOutputLog(`Invalid meta type: ${metaType}`, "ERROR"); + showMessage(`Invalid meta type: ${metaType}`, MessageKind.ERROR, { + logger, + }); return ""; } @@ -380,47 +384,39 @@ export class InsightsConnection { return undefined; } options.responseType = "arraybuffer"; - const results = await window.withProgress( - { - location: ProgressLocation.Notification, - cancellable: true, - }, - async (progress, token) => { - token.onCancellationRequested(() => { - kdbOutputLog(`User cancelled the Datasource Run.`, "WARNING"); - }); - - progress.report({ message: "Query executing..." }); - return await axios(options) - .then((response: any) => { - kdbOutputLog( - `[Datasource RUN] Status: ${response.status}.`, - "INFO", - ); - if (isCompressed(response.data)) { - response.data = uncompress(response.data); - } - return { - error: "", - arrayBuffer: response.data.buffer - ? response.data.buffer - : response.data, - }; - }) - .catch((error: any) => { - kdbOutputLog( - `[Datasource RUN] Status: ${error.response.status}.`, - "INFO", - ); - return { - error: { buffer: error.response.data }, - arrayBuffer: undefined, - }; - }); - }, - ); - return results; + const runner = Runner.create(async () => { + return await axios(options) + .then((response: any) => { + showMessage( + `Datasource run status: ${response.status}.`, + MessageKind.DEBUG, + { logger }, + ); + if (isCompressed(response.data)) { + response.data = uncompress(response.data); + } + return { + error: "", + arrayBuffer: response.data.buffer + ? response.data.buffer + : response.data, + }; + }) + .catch((error: any) => { + showMessage( + `Datasource run status: ${error.response.status}.`, + MessageKind.DEBUG, + { logger, params: [error] }, + ); + return { + error: { buffer: error.response.data }, + arrayBuffer: undefined, + }; + }); + }); + runner.title = "Executing query"; + return await runner.execute(); } } @@ -470,9 +466,10 @@ export class InsightsConnection { const udaReqBody = await retrieveUDAtoCreateReqBody(uda, this); if (udaReqBody.error) { - kdbOutputLog( - `[SCRATCHPAD] Error occurred while creating UDA request body: ${udaReqBody.error}`, - "ERROR", + showMessage( + "Unable to create UDA request body.", + MessageKind.ERROR, + { logger, params: [udaReqBody.error] }, ); return; } @@ -504,46 +501,27 @@ export class InsightsConnection { return; } - await window.withProgress( - { - location: ProgressLocation.Notification, - cancellable: false, - }, - async (progress, token) => { - token.onCancellationRequested(() => { - kdbOutputLog(`User cancelled the scratchpad import.`, "WARNING"); - }); - - progress.report({ message: "Populating scratchpad..." }); - - return await axios(options).then((response: any) => { - if (response.data.error) { - kdbOutputLog( - `[SCRATCHPAD] Error occured while populating scratchpad: ${response.data.errorMsg}`, - "ERROR", - ); - } else { - kdbOutputLog( - `Executed successfully, stored in ${variableName}.`, - "INFO", - ); - kdbOutputLog(`[SCRATCHPAD] Status: ${response.status}`, "INFO"); - kdbOutputLog( - `[SCRATCHPAD] Populated scratchpad with the following params: ${JSON.stringify( - body.params, - )}`, - "INFO", - ); - window.showInformationMessage( - `Executed successfully, stored in ${variableName}.`, - ); - Telemetry.sendEvent( - "Datasource." + dsTypeString + ".Scratchpad.Populated", - ); - } - }); - }, - ); + const runner = Runner.create(async () => { + return await axios(options).then((response: any) => { + if (response.data.error) { + showMessage("Unable to populate scratchpad.", MessageKind.ERROR, { + logger, + params: [response.data.errorMsg], + }); + } else { + showMessage( + `Populated scratchpad, stored in ${variableName}.`, + MessageKind.INFO, + { logger, params: [response.status, body.params] }, + ); + Telemetry.sendEvent( + "Datasource." + dsTypeString + ".Scratchpad.Populated", + ); + } + }); + }); + runner.title = "Populating scratchpad..."; + await runner.execute(); } else { this.noConnectionOrEndpoints(); } @@ -601,36 +579,28 @@ export class InsightsConnection { return; } - const udaResponse = await window.withProgress( - { - location: ProgressLocation.Notification, - cancellable: false, - }, - async (_progress, token) => { - token.onCancellationRequested(() => { - kdbOutputLog(`User cancelled the UDA execution.`, "WARNING"); - }); - - const udaRes = await axios(options).then((response: any) => { - if (response.data.error) { - return response.data; - } else { - kdbOutputLog(`[UDA] Status: ${response.status}`, "INFO"); - if (!response.data.error) { - if (isTableView) { - response.data = JSON.parse( - response.data.data, - ) as StructuredTextResults; - } - return response.data; + const runner = Runner.create(async () => { + const udaRes = await axios(options).then((response: any) => { + if (response.data.error) { + return response.data; + } else { + showMessage(`Status: ${response.status}`, MessageKind.DEBUG, { + logger, + }); + if (!response.data.error) { + if (isTableView) { + response.data = JSON.parse( + response.data.data, + ) as StructuredTextResults; } return response.data; } - }); - return udaRes; - }, - ); - return udaResponse; + return response.data; + } + }); + return udaRes; + }); + return await runner.execute(); } else { this.noConnectionOrEndpoints(); } @@ -684,61 +654,54 @@ export class InsightsConnection { return; } - const spResponse = await window.withProgress( - { - location: ProgressLocation.Notification, - cancellable: false, - }, - async (progress, token) => { - token.onCancellationRequested(() => { - kdbOutputLog(`User cancelled the scratchpad execution.`, "WARNING"); - }); + const runner = Runner.create(async (progress) => { + if (isStarting) { + progress.report({ message: "Starting scratchpad..." }); + } - if (isStarting) { - progress.report({ message: "Starting scratchpad..." }); - } + const spRes = await axios(options).then((response: any) => { + if (response.data.error) { + return response.data; + } else if (query === "") { + showMessage( + `Scratchpad created for connection: ${this.connLabel}.`, + MessageKind.DEBUG, + { logger }, + ); + } else { + showMessage(`Status: ${response.status}`, MessageKind.DEBUG, { + logger, + }); + if (!response.data.error) { + if (isTableView) { + if ( + /* TODO: Workaround for Python structuredText bug */ + !isPython && + this.insightsVersion && + isBaseVersionGreaterOrEqual(this.insightsVersion, 1.12) + ) { + response.data = JSON.parse( + response.data.data, + ) as StructuredTextResults; + } else { + const buffer = new Uint8Array( + response.data.data.map((x: string) => parseInt(x, 16)), + ).buffer; - const spRes = await axios(options).then((response: any) => { - if (response.data.error) { - return response.data; - } else if (query === "") { - kdbOutputLog( - `[SCRATCHPAD] scratchpad created for connection: ${this.connLabel}`, - "INFO", - ); - } else { - kdbOutputLog(`[SCRATCHPAD] Status: ${response.status}`, "INFO"); - if (!response.data.error) { - if (isTableView) { - if ( - /* TODO: Workaround for Python structuredText bug */ - !isPython && - this.insightsVersion && - isBaseVersionGreaterOrEqual(this.insightsVersion, 1.12) - ) { - response.data = JSON.parse( - response.data.data, - ) as StructuredTextResults; - } else { - const buffer = new Uint8Array( - response.data.data.map((x: string) => parseInt(x, 16)), - ).buffer; - - response.data.data = handleWSResults(buffer, isTableView); - response.data.data = handleScratchpadTableRes( - response.data.data, - ); - } + response.data.data = handleWSResults(buffer, isTableView); + response.data.data = handleScratchpadTableRes( + response.data.data, + ); } - return response.data; } return response.data; } - }); - return spRes; - }, - ); - return spResponse; + return response.data; + } + }); + return spRes; + }); + return await runner.execute(); } else { this.noConnectionOrEndpoints(); } @@ -763,43 +726,30 @@ export class InsightsConnection { return; } - return await window.withProgress( - { - location: ProgressLocation.Notification, - cancellable: false, - }, - async (progress, token) => { - token.onCancellationRequested(() => { - kdbOutputLog(`User cancelled the scratchpad reset.`, "WARNING"); + const runner = Runner.create(async (progress) => { + progress.report({ message: "Reseting scratchpad..." }); + const res = await axios(options) + .then((_response: any) => { + showMessage( + `Executed successfully, scratchpad reset at ${this.connLabel} connection.`, + MessageKind.INFO, + { logger }, + ); + Telemetry.sendEvent("Scratchpad.Reseted"); + return true; + }) + .catch((_error: any) => { + showMessage( + `Error occurred while resetting scratchpad in connection ${this.connLabel}, try again.`, + MessageKind.ERROR, + { logger }, + ); return false; }); - progress.report({ message: "Reseting scratchpad..." }); - const res = await axios(options) - .then((_response: any) => { - kdbOutputLog( - `[SCRATCHPAD] Executed successfully, scratchpad reset at ${this.connLabel} connection.`, - "INFO", - ); - window.showInformationMessage( - `Executed successfully, scratchpad reset at ${this.connLabel} connection.`, - ); - Telemetry.sendEvent("Scratchpad.Reseted"); - return true; - }) - .catch((_error: any) => { - kdbOutputLog( - `[SCRATCHPAD] Error occurred while resetting scratchpad in connection ${this.connLabel}, try again.`, - "ERROR", - ); - window.showErrorMessage( - "Error occurred while resetting scratchpad, try again.", - ); - return false; - }); - return res; - }, - ); + return res; + }); + return await runner.execute(); } else { this.noConnectionOrEndpoints(); return false; @@ -807,9 +757,10 @@ export class InsightsConnection { } public noConnectionOrEndpoints(): void { - kdbOutputLog( + showMessage( `No connection or endpoints defined for ${this.connLabel}`, - "ERROR", + MessageKind.ERROR, + { logger }, ); } } diff --git a/src/classes/localConnection.ts b/src/classes/localConnection.ts index f56c7c6d..76ae8927 100644 --- a/src/classes/localConnection.ts +++ b/src/classes/localConnection.ts @@ -12,14 +12,17 @@ */ import * as nodeq from "node-q"; -import { commands, window } from "vscode"; +import { commands } from "vscode"; import { ext } from "../extensionVariables"; import { QueryResult, QueryResultType } from "../models/queryResult"; -import { delay, kdbOutputLog } from "../utils/core"; +import { delay } from "../utils/core"; import { convertStringToArray, handleQueryResults } from "../utils/execution"; +import { MessageKind, showMessage } from "../utils/notifications"; import { queryWrapper } from "../utils/queryUtils"; +const logger = "localConnection"; + export class LocalConnection { public connected: boolean; public connLabel: string; @@ -84,38 +87,43 @@ export class LocalConnection { ext.serverProvider.reload(); if (this.connLabel.endsWith("[local]")) { - window - .showErrorMessage( - `Connection to server ${this.options.host}:${this.options.port} failed.`, - "Start q process", - ) - .then((res) => { - if (res) { - commands.executeCommand( - "kdb.connections.localProcess.start", - ext.connectionsList.find( - (conn) => conn.label === this.connLabel, - ), - ); - } - }); + showMessage( + `Connection to server ${this.options.host}:${this.options.port} failed.`, + MessageKind.ERROR, + {}, + "Start q process", + ).then((res) => { + if (res) { + commands.executeCommand( + "kdb.connections.localProcess.start", + ext.connectionsList.find( + (conn) => conn.label === this.connLabel, + ), + ); + } + }); } else { - window.showErrorMessage( + showMessage( `Connection to server ${this.options.host}:${this.options.port} failed.`, + MessageKind.ERROR, + { logger }, ); } - kdbOutputLog( - `Connection to server ${this.options.host}:${this.options.port} failed! Details: ${err?.message}`, - "CONNECTION", + showMessage( + `Connection to server ${this.options.host}:${this.options.port} failed.`, + MessageKind.ERROR, + { logger, params: [err] }, ); + return; } conn.addListener("close", () => { commands.executeCommand("kdb.connections.disconnect", this.connLabel); - kdbOutputLog( + showMessage( `Connection closed: ${this.options.host}:${this.options.port}`, - "INFO", + MessageKind.DEBUG, + { logger }, ); ext.outputChannel.show(); }); @@ -306,8 +314,10 @@ export class LocalConnection { '{[q] t:system"T";tm:@[{$[x>0;[system"T ",string x;1b];0b]};0;{0b}];r:$[tm;@[0;(q;::);{[tm; t; msgs] if[tm;system"T ",string t];\'msgs}[tm;t]];@[q;::;{\'x}]];if[tm;system"T ",string t];r}{do[1000;2+2];{@[{.z.ide.ns.r1:x;:.z.ide.ns.r1};x;{r:y;:r}[;x]]}({:x!{![sv[`;] each x cross `Tables`Functions`Variables; system each "afv" cross enlist[" "] cross enlist string x]} each x} [{raze x,.z.s\'[{x where{@[{1#get x};x;`]~1#.q}\'[x]}` sv\'x,\'key x]}`]),(enlist `.z)!flip (`.z.Tables`.z.Functions`.z.Variables)!(enlist 0#`;enlist `ac`bm`exit`pc`pd`pg`ph`pi`pm`po`pp`ps`pw`vs`ts`s`wc`wo`ws;enlist `a`b`e`f`h`i`k`K`l`o`q`u`w`W`x`X`n`N`p`P`z`Z`t`T`d`D`c`zd)}'; this.connection?.k(globalQuery, (err, result) => { if (err) { - window.showErrorMessage( - `Failed to retrieve kdb+ global variables: '${err.message}`, + showMessage( + "Failed to retrieve kdb+ global variables.", + MessageKind.ERROR, + { logger, params: [err] }, ); return; } @@ -350,8 +360,10 @@ export class LocalConnection { const reservedQuery = ".Q.res"; this.connection?.k(reservedQuery, (err, result) => { if (err) { - window.showErrorMessage( - `Failed to retrieve kdb+ reserved keywords: '${err.message}`, + showMessage( + "Failed to retrieve kdb+ reserved keywords.", + MessageKind.ERROR, + { logger, params: [err] }, ); return; } diff --git a/src/commands/buildToolsCommand.ts b/src/commands/buildToolsCommand.ts index c9a0e1b7..f3b4294a 100644 --- a/src/commands/buildToolsCommand.ts +++ b/src/commands/buildToolsCommand.ts @@ -18,15 +18,14 @@ import Path from "path"; import { Diagnostic, DiagnosticSeverity, - ProgressLocation, Range, TextDocument, Uri, - window, workspace, } from "vscode"; import { ext } from "../extensionVariables"; +import { Runner } from "../utils/notifications"; const cache = new Map>(); @@ -182,39 +181,34 @@ const severity: { [key: string]: DiagnosticSeverity } = { }; function lint(document: TextDocument) { - return window.withProgress( - { - title: "Linting", - location: ProgressLocation.Window, - cancellable: true, - }, - async (_progress, token) => { - try { - const results = await getLinterResults(document.uri); - if (token.isCancellationRequested) { - cache.delete(document.uri.path); - return []; - } - return results.map((result) => { - const diagnostic = new Diagnostic( - new Range( - result.startLine - 1, - result.startCol - 1, - result.endLine - 1, - result.endCol, - ), - result.description, - severity[result.errorClass], - ); - diagnostic.source = "qlint"; - diagnostic.code = result.label; - return diagnostic; - }); - } catch (error) { - throw new Error(`Linting Failed ${error}`); + const runner = Runner.create(async (_, token) => { + try { + const results = await getLinterResults(document.uri); + if (token.isCancellationRequested) { + cache.delete(document.uri.path); + return []; } - }, - ); + return results.map((result) => { + const diagnostic = new Diagnostic( + new Range( + result.startLine - 1, + result.startCol - 1, + result.endLine - 1, + result.endCol, + ), + result.description, + severity[result.errorClass], + ); + diagnostic.source = "qlint"; + diagnostic.code = result.label; + return diagnostic; + }); + } catch (error) { + throw new Error(`Linting Failed ${error}`); + } + }); + runner.title = "Linting"; + return runner.execute(); } async function setDiagnostics(document: TextDocument) { diff --git a/src/commands/dataSourceCommand.ts b/src/commands/dataSourceCommand.ts index 5afb70bb..8c14ee42 100644 --- a/src/commands/dataSourceCommand.ts +++ b/src/commands/dataSourceCommand.ts @@ -33,17 +33,14 @@ import { scratchpadVariableInput } from "../models/items/server"; import { UDARequestBody } from "../models/uda"; import { DataSourcesPanel } from "../panels/datasource"; import { ConnectionManagementService } from "../services/connectionManagerService"; -import { - kdbOutputLog, - noSelectedConnectionAction, - offerConnectAction, -} from "../utils/core"; +import { noSelectedConnectionAction, offerConnectAction } from "../utils/core"; import { checkIfTimeParamIsCorrect, convertTimeToTimestamp, createKdbDataSourcesFolder, getConnectedInsightsNode, } from "../utils/dataSource"; +import { MessageKind, showMessage } from "../utils/notifications"; import { addQueryHistory, generateQSqlBody, @@ -55,6 +52,8 @@ import { Telemetry } from "../utils/telemetryClient"; import { retrieveUDAtoCreateReqBody } from "../utils/uda"; import { validateScratchpadOutputVariableName } from "../validators/interfaceValidator"; +const logger = "dataSourceCommands"; + export async function addDataSource(): Promise { const kdbDataSourcesFolderPath = createKdbDataSourcesFolder(); @@ -74,8 +73,10 @@ export async function addDataSource(): Promise { defaultDataSourceContent.insightsNode = insightsNode; fs.writeFileSync(filePath, JSON.stringify(defaultDataSourceContent)); - window.showInformationMessage( + showMessage( `Created ${fileName} in ${kdbDataSourcesFolderPath}.`, + MessageKind.INFO, + { logger }, ); Telemetry.sendEvent("Datasource.Created"); } @@ -115,9 +116,10 @@ export async function populateScratchpad( qenvEnabled === "Enabled", ); } else { - kdbOutputLog( - `[DATASOURCE] Invalid scratchpad output variable name: ${outputVariable}`, - "ERROR", + showMessage( + `Invalid scratchpad output variable name: ${outputVariable}`, + MessageKind.ERROR, + { logger }, ); } }); @@ -155,9 +157,10 @@ export async function runDataSource( dataSourceForm.insightsNode = getConnectedInsightsNode(); const fileContent = dataSourceForm; - kdbOutputLog( - `[DATASOURCE] Running ${fileContent.name} datasource...`, - "INFO", + showMessage( + `Running ${fileContent.name} datasource...`, + MessageKind.DEBUG, + { logger }, ); let res: any; const selectedType = getSelectedType(fileContent); @@ -189,12 +192,17 @@ export async function runDataSource( const query = getQuery(fileContent, selectedType); if (!success) { - window.showErrorMessage(res.error); + showMessage("Datasource run failed.", MessageKind.ERROR, { + logger, + params: [res.error], + }); } if (ext.isResultsTabVisible) { if (success) { const resultCount = typeof res === "string" ? "0" : res.rows.length; - kdbOutputLog(`[DATASOURCE] Results: ${resultCount} rows`, "INFO"); + showMessage(`Results: ${resultCount} rows`, MessageKind.DEBUG, { + logger, + }); } else if (!success) { res = res.errorMsg ? res.errorMsg : res.error; } @@ -208,9 +216,10 @@ export async function runDataSource( ); } else { if (success) { - kdbOutputLog( - `[DATASOURCE] Results is a string with length: ${res.length}`, - "INFO", + showMessage( + `Results is a string with length: ${res.length}`, + MessageKind.DEBUG, + { logger }, ); } else if (res.error) { res = res.errorMsg ? res.errorMsg : res.error; @@ -228,8 +237,10 @@ export async function runDataSource( addDStoQueryHistory(dataSourceForm, success, connLabel, executorName); } } catch (error) { - window.showErrorMessage((error as Error).message); - kdbOutputLog(`[DATASOURCE] ${(error as Error).message}`, "ERROR", true); + showMessage(`Datasource error: ${error}.`, MessageKind.ERROR, { + logger, + params: [error], + }); DataSourcesPanel.running = false; } finally { DataSourcesPanel.running = false; @@ -280,8 +291,9 @@ export async function runApiDataSource( fileContent.dataSource.api.endTS, ); if (!isTimeCorrect) { - window.showErrorMessage( - "The time parameters(startTS and endTS) are not correct, please check the format or if the startTS is before the endTS", + showMessage( + "The time parameters (startTS and endTS) are not correct, please check the format or if the startTS is before the endTS", + MessageKind.ERROR, ); return; } @@ -444,7 +456,10 @@ export async function runUDADataSource( const udaReqBody = await retrieveUDAtoCreateReqBody(uda, selectedConn); if (udaReqBody.error) { - kdbOutputLog(`[DATASOURCE] Error: ${udaReqBody.error}`, "ERROR", true); + showMessage(`Datasource error.`, MessageKind.ERROR, { + logger, + params: [udaReqBody.error], + }); return udaReqBody; } @@ -491,11 +506,10 @@ export function parseError(error: GetDataError) { if (error instanceof Object && error.buffer) { return handleWSError(error.buffer); } else { - kdbOutputLog( - `[DATASOURCE] Error: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`, - "ERROR", - true, - ); + showMessage(`Datasource error.`, MessageKind.ERROR, { + logger, + params: [error], + }); return { error, }; diff --git a/src/commands/installTools.ts b/src/commands/installTools.ts index e3881d9d..d4a0da40 100644 --- a/src/commands/installTools.ts +++ b/src/commands/installTools.ts @@ -20,7 +20,6 @@ import { join } from "path"; import { ConfigurationTarget, InputBoxOptions, - ProgressLocation, QuickPickItem, Uri, commands, @@ -56,19 +55,20 @@ import { getServerName, getServers, getWorkspaceFolder, - kdbOutputLog, removeLocalConnectionStatus, saveLocalProcessObj, updateServers, } from "../utils/core"; import { executeCommand } from "../utils/cpUtils"; +import { MessageKind, Runner, showMessage } from "../utils/notifications"; import { openUrl } from "../utils/openUrl"; import { Telemetry } from "../utils/telemetryClient"; import { validateServerPort } from "../validators/kdbValidator"; +const logger = "install"; + export async function installTools(): Promise { let file: Uri[] | undefined; - let runtimeUrl: string; await commands.executeCommand("notifications.clearAll"); await commands.executeCommand("welcome.goBack"); @@ -82,17 +82,17 @@ export async function installTools(): Promise { if (licenseTypeResult?.label === licenseAquire) { let licenseCancel; await openUrl(ext.kdbInstallUrl); - await window - .showInformationMessage( - licenseWorkflow.prompt, - licenseWorkflow.option1, - licenseWorkflow.option2, - ) - .then(async (res) => { - if (res === licenseWorkflow.option2) { - licenseCancel = true; - } - }); + await showMessage( + licenseWorkflow.prompt, + MessageKind.INFO, + {}, + licenseWorkflow.option1, + licenseWorkflow.option2, + ).then(async (res) => { + if (res === licenseWorkflow.option2) { + licenseCancel = true; + } + }); if (licenseCancel) return; } @@ -131,172 +131,161 @@ export async function installTools(): Promise { throw new Error(); } - window - .withProgress( - { - location: ProgressLocation.Notification, - title: "Installation of q", - cancellable: true, - }, - async (progress, token) => { - token.onCancellationRequested(() => { - kdbOutputLog("[Install] User cancelled the installation.", "INFO"); - }); + const runner = Runner.create(async (progress, token) => { + token.onCancellationRequested(() => { + showMessage("User cancelled the installation.", MessageKind.DEBUG, { + logger, + }); + }); - progress.report({ increment: 0 }); + progress.report({ increment: 0 }); - // download the binaries - progress.report({ increment: 20, message: "Getting the binaries..." }); - const osFile = getOsFile(); - if (osFile === undefined) { - kdbOutputLog( - "[Install] Unsupported operating system, unable to download binaries for this.", - "ERROR", - ); + // download the binaries + progress.report({ increment: 20, message: "Getting the binaries..." }); + const osFile = getOsFile(); + if (osFile === undefined) { + showMessage( + "Unsupported operating system, unable to download binaries.", + MessageKind.ERROR, + { logger }, + ); + Telemetry.sendException( + new Error("Unsupported operating system, unable to download binaries"), + ); + } else { + const gpath = join(ext.context.globalStorageUri.fsPath, osFile); + if (!existsSync(gpath)) { + const runtimeUrl = `${ext.kdbDownloadPrefixUrl}${osFile}`; + const response = await fetch(runtimeUrl); + if (response.status > 200) { Telemetry.sendException( - new Error( - "Unsupported operating system, unable to download binaries", - ), + new Error("Invalid or unavailable download url."), ); - } else { - const gpath = join(ext.context.globalStorageUri.fsPath, osFile); - if (!existsSync(gpath)) { - const response = await fetch( - `${ext.kdbDownloadPrefixUrl}${osFile}`, - ); - if (response.status > 200) { - Telemetry.sendException( - new Error("Invalid or unavailable download url."), - ); - kdbOutputLog( - `[Install] Invalid or unavailable download url: ${runtimeUrl}`, - "ERROR", - ); - window.showErrorMessage( - `Invalid or unavailable download url: ${runtimeUrl}`, - ); - exit(1); - } - await ensureDir(ext.context.globalStorageUri.fsPath); - await writeFile(gpath, Buffer.from(await response.arrayBuffer())); - } - await extract(gpath, { dir: ext.context.globalStorageUri.fsPath }); - } - // move the license file - progress.report({ increment: 30, message: "Moving license file..." }); - await delay(500); + showMessage( + `Invalid or unavailable download url: ${runtimeUrl}`, + MessageKind.ERROR, + { logger }, + ); + exit(1); + } await ensureDir(ext.context.globalStorageUri.fsPath); - await copy( - file![0].fsPath, - join(ext.context.globalStorageUri.fsPath, ext.kdbLicName), - ); + await writeFile(gpath, Buffer.from(await response.arrayBuffer())); + } + await extract(gpath, { dir: ext.context.globalStorageUri.fsPath }); + } - // add the env var for the process - progress.report({ - increment: 60, - message: "Setting up environment...", - }); - await delay(500); - env.QHOME = ext.context.globalStorageUri.fsPath; + // move the license file + progress.report({ increment: 30, message: "Moving license file..." }); + await delay(500); + await ensureDir(ext.context.globalStorageUri.fsPath); + await copy( + file![0].fsPath, + join(ext.context.globalStorageUri.fsPath, ext.kdbLicName), + ); - // persist the QHOME to global settings - await workspace - .getConfiguration() - .update("kdb.qHomeDirectory", env.QHOME, ConfigurationTarget.Global); + // add the env var for the process + progress.report({ + increment: 60, + message: "Setting up environment...", + }); + await delay(500); + env.QHOME = ext.context.globalStorageUri.fsPath; - // update walkthrough - const QHOME = workspace - .getConfiguration() - .get("kdb.qHomeDirectory"); - if (QHOME) { - env.QHOME = QHOME; - if (!pathExists(env.QHOME)) { - kdbOutputLog("[Install] QHOME path stored is empty", "ERROR"); - } - await writeFile( - join(__dirname, "qinstall.md"), - `# q runtime installed location: \n### ${QHOME}`, - ); - kdbOutputLog( - `[Install] Installation of q found here: ${QHOME}`, - "INFO", - ); - } - }, - ) - .then(async () => { - window - .showInformationMessage( - onboardingWorkflow.prompt(ext.context.globalStorageUri.fsPath), - onboardingWorkflow.option1, - onboardingWorkflow.option2, - ) - .then(async (startResult) => { - if (startResult === onboardingWorkflow.option1) { - const portInput: InputBoxOptions = { - prompt: onboardingInput.prompt, - placeHolder: onboardingInput.placeholder, - ignoreFocusOut: true, - validateInput: (value: string | undefined) => - validateServerPort(value), - }; - window.showInputBox(portInput).then(async (port) => { - if (port) { - let servers: Server | undefined = getServers(); - if ( - servers != undefined && - servers[getKeyForServerName("local")] - ) { - Telemetry.sendEvent( - `Server localhost:${port} already exists in configuration store.`, - ); - await window.showErrorMessage( - `Server localhost:${port} already exists.`, - ); - } else { - const key = "local"; - if (servers === undefined) { - servers = { - key: { - auth: false, - serverName: "localhost", - serverPort: port, - serverAlias: "local", - managed: true, - tls: false, - }, - }; - await addLocalConnectionContexts(getServerName(servers[0])); - } else { - servers[key] = { - auth: false, - serverName: "localhost", - serverPort: port, - serverAlias: "local", - managed: true, - tls: false, - }; - await addLocalConnectionContexts( - getServerName(servers[key]), - ); - } - await updateServers(servers); - const newServers = getServers(); - if (newServers != undefined) { - ext.serverProvider.refresh(newServers); - } - } - } - await startLocalProcessByServerName( - `localhost:${port} [local]`, - getKeyForServerName("local"), - Number(port), + // persist the QHOME to global settings + await workspace + .getConfiguration() + .update("kdb.qHomeDirectory", env.QHOME, ConfigurationTarget.Global); + + // update walkthrough + const QHOME = workspace + .getConfiguration() + .get("kdb.qHomeDirectory"); + if (QHOME) { + env.QHOME = QHOME; + if (!pathExists(env.QHOME)) { + showMessage("QHOME path stored is empty", MessageKind.ERROR, { + logger, + }); + } + await writeFile( + join(__dirname, "qinstall.md"), + `# q runtime installed location: \n### ${QHOME}`, + ); + showMessage(`Installation of q found here: ${QHOME}`, MessageKind.DEBUG, { + logger, + }); + } + }); + runner.title = "Installing q..."; + runner.execute().then(async () => { + showMessage( + onboardingWorkflow.prompt(ext.context.globalStorageUri.fsPath), + MessageKind.INFO, + {}, + onboardingWorkflow.option1, + onboardingWorkflow.option2, + ).then(async (startResult) => { + if (startResult === onboardingWorkflow.option1) { + const portInput: InputBoxOptions = { + prompt: onboardingInput.prompt, + placeHolder: onboardingInput.placeholder, + ignoreFocusOut: true, + validateInput: (value: string | undefined) => + validateServerPort(value), + }; + window.showInputBox(portInput).then(async (port) => { + if (port) { + let servers: Server | undefined = getServers(); + if (servers != undefined && servers[getKeyForServerName("local")]) { + Telemetry.sendEvent( + `Server localhost:${port} already exists in configuration store.`, ); - }); + showMessage( + `Server localhost:${port} already exists.`, + MessageKind.ERROR, + ); + } else { + const key = "local"; + if (servers === undefined) { + servers = { + key: { + auth: false, + serverName: "localhost", + serverPort: port, + serverAlias: "local", + managed: true, + tls: false, + }, + }; + await addLocalConnectionContexts(getServerName(servers[0])); + } else { + servers[key] = { + auth: false, + serverName: "localhost", + serverPort: port, + serverAlias: "local", + managed: true, + tls: false, + }; + await addLocalConnectionContexts(getServerName(servers[key])); + } + await updateServers(servers); + const newServers = getServers(); + if (newServers != undefined) { + ext.serverProvider.refresh(newServers); + } + } } + await startLocalProcessByServerName( + `localhost:${port} [local]`, + getKeyForServerName("local"), + Number(port), + ); }); + } }); + }); } export async function startLocalProcessByServerName( @@ -320,7 +309,7 @@ export async function startLocalProcessByServerName( ); } catch { await removeLocalConnectionStatus(serverName); - window.showErrorMessage("Error starting q process."); + showMessage("Error starting q process.", MessageKind.ERROR); } } @@ -334,10 +323,11 @@ export async function startLocalProcess(viewItem: KdbNode): Promise { export async function stopLocalProcess(viewItem: KdbNode): Promise { ext.localProcessObjects[viewItem.children[0]].kill(); - kdbOutputLog( + showMessage( `Child process id ${ext.localProcessObjects[viewItem.children[0]] .pid!} removed in cache.`, - "INFO", + MessageKind.DEBUG, + { logger }, ); await removeLocalConnectionStatus(`${getServerName(viewItem.details)}`); } @@ -346,8 +336,9 @@ export async function stopLocalProcessByServerName( serverName: string, ): Promise { ext.localProcessObjects[serverName].kill(); - kdbOutputLog( + showMessage( `Child process id ${ext.localProcessObjects[serverName].pid!} removed in cache.`, - "INFO", + MessageKind.DEBUG, + { logger }, ); } diff --git a/src/commands/serverCommand.ts b/src/commands/serverCommand.ts index f604275b..fe33e3e5 100644 --- a/src/commands/serverCommand.ts +++ b/src/commands/serverCommand.ts @@ -18,7 +18,6 @@ import * as url from "url"; import { CancellationToken, Position, - ProgressLocation, Range, Uri, ViewColumn, @@ -65,7 +64,6 @@ import { getKeyForServerName, getServerName, getServers, - kdbOutputLog, offerReconnectionAfterEdit, updateInsights, updateServers, @@ -73,6 +71,7 @@ import { import { refreshDataSourcesPanel } from "../utils/dataSource"; import { decodeQUTF } from "../utils/decode"; import { ExecutionConsole } from "../utils/executionConsole"; +import { MessageKind, Runner, showMessage } from "../utils/notifications"; import { openUrl } from "../utils/openUrl"; import { checkIfIsDatasource, @@ -94,6 +93,8 @@ import { validateServerUsername, } from "../validators/kdbValidator"; +const logger = "serverCommand"; + export async function addNewConnection(): Promise { NewConnectionPannel.close(); NewConnectionPannel.render(ext.context.extensionUri); @@ -115,7 +116,7 @@ export async function addInsightsConnection( ) { const aliasValidation = validateServerAlias(insightsData.alias, false); if (aliasValidation) { - window.showErrorMessage(aliasValidation); + showMessage(aliasValidation, MessageKind.ERROR); return; } if (insightsData.alias === undefined || insightsData.alias === "") { @@ -128,8 +129,9 @@ export async function addInsightsConnection( insights != undefined && insights[getKeyForServerName(insightsData.alias)] ) { - await window.showErrorMessage( + showMessage( `Insights instance named ${insightsData.alias} already exists.`, + MessageKind.ERROR, ); return; } else { @@ -169,8 +171,10 @@ export async function addInsightsConnection( ext.serverProvider.refreshInsights(newInsights); Telemetry.sendEvent("Connection.Created.Insights"); } - window.showInformationMessage( + + showMessage( `Added Insights connection: ${insightsData.alias}`, + MessageKind.INFO, ); NewConnectionPannel.close(); @@ -188,7 +192,7 @@ export async function editInsightsConnection( ? undefined : validateServerAlias(insightsData.alias, false); if (aliasValidation) { - window.showErrorMessage(aliasValidation); + showMessage(aliasValidation, MessageKind.ERROR); return; } const isConnectedConn = isConnected(oldAlias); @@ -205,14 +209,16 @@ export async function editInsightsConnection( ? insights[getKeyForServerName(insightsData.alias)] : undefined; if (newAliasExists) { - await window.showErrorMessage( + showMessage( `Insights instance named ${insightsData.alias} already exists.`, + MessageKind.ERROR, ); return; } else { if (!oldInsights) { - await window.showErrorMessage( + showMessage( `Insights instance named ${oldAlias} does not exist.`, + MessageKind.ERROR, ); return; } else { @@ -263,8 +269,10 @@ export async function editInsightsConnection( offerReconnectionAfterEdit(insightsData.alias); } } - window.showInformationMessage( + + showMessage( `Edited Insights connection: ${insightsData.alias}`, + MessageKind.INFO, ); NewConnectionPannel.close(); @@ -282,7 +290,7 @@ export async function addAuthConnection( ): Promise { const validUsername = validateServerUsername(username); if (validUsername) { - window.showErrorMessage(validUsername); + showMessage(validUsername, MessageKind.ERROR); return; } if (password?.trim()?.length) { @@ -324,17 +332,17 @@ export async function enableTLS(serverKey: string): Promise { // validate if TLS is possible if (ext.openSslVersion === null) { - window - .showErrorMessage( - "OpenSSL not found, please ensure this is installed", - "More Info", - "Cancel", - ) - .then(async (result) => { - if (result === "More Info") { - await openUrl("https://code.kx.com/q/kb/ssl/"); - } - }); + showMessage( + "OpenSSL not found, please ensure this is installed", + MessageKind.ERROR, + {}, + "More Info", + "Cancel", + ).then(async (result) => { + if (result === "More Info") { + await openUrl("https://code.kx.com/q/kb/ssl/"); + } + }); return; } if (servers && servers[serverKey]) { @@ -346,8 +354,10 @@ export async function enableTLS(serverKey: string): Promise { } return; } - window.showErrorMessage( + showMessage( "Server not found, please ensure this is a correct server", + MessageKind.ERROR, + {}, "Cancel", ); } @@ -361,15 +371,15 @@ export async function addKdbConnection( const hostnameValidation = validateServerName(kdbData.serverName); const portValidation = validateServerPort(kdbData.serverPort); if (aliasValidation) { - window.showErrorMessage(aliasValidation); + showMessage(aliasValidation, MessageKind.ERROR); return; } if (hostnameValidation) { - window.showErrorMessage(hostnameValidation); + showMessage(hostnameValidation, MessageKind.ERROR); return; } if (portValidation) { - window.showErrorMessage(portValidation); + showMessage(portValidation, MessageKind.ERROR); return; } let servers: Server | undefined = getServers(); @@ -378,8 +388,9 @@ export async function addKdbConnection( servers != undefined && servers[getKeyForServerName(kdbData.serverAlias || "")] ) { - await window.showErrorMessage( + showMessage( `Server name ${kdbData.serverAlias} already exists.`, + MessageKind.ERROR, ); } else { const key = kdbData.serverAlias || ""; @@ -425,8 +436,10 @@ export async function addKdbConnection( if (kdbData.auth) { addAuthConnection(key, kdbData.username!, kdbData.password!); } - window.showInformationMessage( + + showMessage( `Added kdb connection: ${kdbData.serverAlias}`, + MessageKind.INFO, ); NewConnectionPannel.close(); @@ -448,15 +461,15 @@ export async function editKdbConnection( const hostnameValidation = validateServerName(kdbData.serverName); const portValidation = validateServerPort(kdbData.serverPort); if (aliasValidation) { - window.showErrorMessage(aliasValidation); + showMessage(aliasValidation, MessageKind.ERROR); return; } if (hostnameValidation) { - window.showErrorMessage(hostnameValidation); + showMessage(hostnameValidation, MessageKind.ERROR); return; } if (portValidation) { - window.showErrorMessage(portValidation); + showMessage(portValidation, MessageKind.ERROR); return; } const isConnectedConn = isConnected(oldAlias); @@ -470,14 +483,16 @@ export async function editKdbConnection( ? servers[getKeyForServerName(kdbData.serverAlias)] : undefined; if (newAliasExists) { - await window.showErrorMessage( + showMessage( `KDB instance named ${kdbData.serverAlias} already exists.`, + MessageKind.ERROR, ); return; } else { if (!oldServer) { - await window.showErrorMessage( + showMessage( `KDB instance named ${oldAlias} does not exist.`, + MessageKind.ERROR, ); return; } else { @@ -533,8 +548,10 @@ export async function editKdbConnection( offerReconnectionAfterEdit(connLabelToReconn); } } - window.showInformationMessage( + + showMessage( `Edited KDB connection: ${kdbData.serverAlias}`, + MessageKind.INFO, ); if (oldKey !== newKey) { removeConnFromLabels(oldKey); @@ -571,7 +588,7 @@ export async function importConnections() { const fileUri = await window.showOpenDialog(options); if (!fileUri || fileUri.length === 0) { - kdbOutputLog("[IMPORT CONNECTION]No file selected", "ERROR"); + showMessage("No file selected.", MessageKind.ERROR, { logger }); return; } const filePath = fileUri[0].fsPath; @@ -581,24 +598,24 @@ export async function importConnections() { try { importedConnections = JSON.parse(fileContent); } catch { - kdbOutputLog("[IMPORT CONNECTION]Invalid JSON format", "ERROR"); + showMessage("Invalid JSON format.", MessageKind.ERROR, { logger }); return; } if (!isValidExportedConnections(importedConnections)) { - kdbOutputLog( - "[IMPORT CONNECTION]JSON does not match the required format", - "ERROR", - ); + showMessage("JSON does not match the required format.", MessageKind.ERROR, { + logger, + }); 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", + showMessage( + "There is no KDB or Insights connections to import in this JSON file.", + MessageKind.ERROR, + { logger }, ); return; } @@ -630,8 +647,10 @@ export async function addImportedConnections( let res: "Duplicate" | "Overwrite" | "Cancel" | undefined = "Duplicate"; if (hasDuplicates) { - res = await window.showInformationMessage( + res = await showMessage( "You are importing connections with the same name. Would you like to duplicate, overwrite or cancel the import?", + MessageKind.INFO, + {}, "Duplicate", "Overwrite", "Cancel", @@ -701,8 +720,9 @@ export async function addImportedConnections( ext.serverProvider.refresh(config); } - kdbOutputLog("[IMPORT CONNECTION]Connections imported successfully", "INFO"); - window.showInformationMessage("Connections imported successfully"); + showMessage("Connections imported successfully.", MessageKind.INFO, { + logger, + }); } export async function removeConnection(viewItem: KdbNode | InsightsNode) { @@ -720,7 +740,7 @@ export async function connect(connLabel: string): Promise { ExecutionConsole.start(); const viewItem = connMngService.retrieveConnection(connLabel); if (viewItem === undefined) { - window.showErrorMessage("Connection not found"); + showMessage("Connection not found.", MessageKind.ERROR); return; } @@ -729,17 +749,17 @@ export async function connect(connLabel: string): Promise { // check for TLS support if (viewItem.details.tls) { if (!(await checkOpenSslInstalled())) { - window - .showInformationMessage( - "TLS support requires OpenSSL to be installed.", - "More Info", - "Cancel", - ) - .then(async (result) => { - if (result === "More Info") { - await openUrl("https://code.kx.com/q/kb/ssl/"); - } - }); + showMessage( + "TLS support requires OpenSSL to be installed.", + MessageKind.INFO, + {}, + "More Info", + "Cancel", + ).then(async (result) => { + if (result === "More Info") { + await openUrl("https://code.kx.com/q/kb/ssl/"); + } + }); } } } @@ -796,25 +816,20 @@ export async function executeQuery( isWorkbook: boolean, isFromConnTree?: boolean, ): Promise { - await window.withProgress( - { - cancellable: true, - location: ProgressLocation.Window, - title: `Executing query (${executorName})`, - }, - async (_progress, token) => { - await _executeQuery( - query, - connLabel, - executorName, - context, - isPython, - isWorkbook, - isFromConnTree, - token, - ); - }, + const runner = Runner.create((_, token) => + _executeQuery( + query, + connLabel, + executorName, + context, + isPython, + isWorkbook, + isFromConnTree, + token, + ), ); + runner.title = `Executing query (${executorName})`; + await runner.execute(); } export async function _executeQuery( @@ -831,9 +846,9 @@ export async function _executeQuery( const queryConsole = ExecutionConsole.start(); if (connLabel === "") { if (ext.activeConnection === undefined) { - kdbOutputLog( + showMessage( "No active connection found. Connect to one connection.", - "ERROR", + MessageKind.ERROR, ); return undefined; } else { @@ -842,8 +857,7 @@ export async function _executeQuery( } const isConnected = connMngService.isConnected(connLabel); if (!isConnected) { - window.showInformationMessage("The selected connection is not connected."); - kdbOutputLog("The selected connection is not connected.", "ERROR"); + showMessage("The selected connection is not connected.", MessageKind.ERROR); return undefined; } @@ -1090,7 +1104,7 @@ export function copyQuery(queryHistoryElement: QueryHistory) { typeof queryHistoryElement.query === "string" ) { env.clipboard.writeText(queryHistoryElement.query); - window.showInformationMessage("Query copied to clipboard."); + showMessage("Query copied to clipboard.", MessageKind.INFO); } } @@ -1101,8 +1115,9 @@ export async function loadServerObjects(): Promise { ext.activeConnection.connected === false || ext.activeConnection instanceof InsightsConnection ) { - window.showInformationMessage( + showMessage( "Please connect to a KDB instance to view the objects", + MessageKind.INFO, ); return new Array(); } @@ -1138,7 +1153,9 @@ export async function openMeta(node: MetaObjectPayloadNode | InsightsMetaNode) { viewColumn: ViewColumn.One, }); } else { - kdbOutputLog("[META] Meta content not found", "ERROR"); + showMessage("Meta content not found.", MessageKind.ERROR, { + logger, + }); } } @@ -1150,9 +1167,10 @@ export async function exportConnections(connLabel?: string) { }); if (!exportAuth) { - kdbOutputLog( - "[EXPORT CONNECTIONS] Export operation was cancelled by the user", - "INFO", + showMessage( + "Export operation was cancelled by the user.", + MessageKind.DEBUG, + { logger }, ); return; } @@ -1174,11 +1192,9 @@ export async function exportConnections(connLabel?: string) { 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}`); + showMessage(`Error saving file: ${err.message}`, MessageKind.ERROR, { + logger, + }); } else { workspace.openTextDocument(uri).then((document) => { window.showTextDocument(document, { preview: false }); @@ -1186,16 +1202,16 @@ export async function exportConnections(connLabel?: string) { } }); } else { - kdbOutputLog( - "[EXPORT CONNECTIONS] Save operation was cancelled by the user", - "INFO", + showMessage( + "Save operation was cancelled by the user.", + MessageKind.DEBUG, + { logger }, ); } } else { - kdbOutputLog( - "[EXPORT CONNECTIONS] No connections found to be exported", - "ERROR", - ); + showMessage("No connections found to be exported.", MessageKind.ERROR, { + logger, + }); } } diff --git a/src/commands/walkthroughCommand.ts b/src/commands/walkthroughCommand.ts index 68a95a37..c649a1a9 100644 --- a/src/commands/walkthroughCommand.ts +++ b/src/commands/walkthroughCommand.ts @@ -11,11 +11,13 @@ * specific language governing permissions and limitations under the License. */ -import { window, workspace } from "vscode"; +import { workspace } from "vscode"; + +import { MessageKind, showMessage } from "../utils/notifications"; export async function showInstallationDetails(): Promise { const QHOME = await workspace .getConfiguration() .get("kdb.qHomeDirectory"); - window.showInformationMessage(`q runtime installed path: ${QHOME}`); + showMessage(`q runtime installed path: ${QHOME}`, MessageKind.INFO); } diff --git a/src/commands/workspaceCommand.ts b/src/commands/workspaceCommand.ts index 06481fa4..f9821ef1 100644 --- a/src/commands/workspaceCommand.ts +++ b/src/commands/workspaceCommand.ts @@ -33,10 +33,13 @@ import { ExecutionTypes } from "../models/execution"; import { MetaDap } from "../models/meta"; import { ConnectionManagementService } from "../services/connectionManagerService"; import { InsightsNode, KdbNode, LabelNode } from "../services/kdbTreeProvider"; -import { kdbOutputLog, offerConnectAction } from "../utils/core"; +import { offerConnectAction } from "../utils/core"; import { importOldDsFiles, oldFilesExists } from "../utils/dataSource"; +import { MessageKind, showMessage } from "../utils/notifications"; import { normalizeAssemblyTarget } from "../utils/shared"; +const logger = "workspaceCommand"; + function setRealActiveTextEditor(editor?: TextEditor | undefined) { if (editor) { const scheme = editor.document.uri.scheme; @@ -404,9 +407,7 @@ export async function importOldDSFiles() { if (ext.oldDSformatExists) { const folders = workspace.workspaceFolders; if (!folders) { - window.showErrorMessage( - "No workspace folder found. Please open a workspace folder.", - ); + showMessage("No workspace folder found.", MessageKind.ERROR); return; } return await window.withProgress( @@ -416,9 +417,10 @@ export async function importOldDSFiles() { }, async (progress, token) => { token.onCancellationRequested(() => { - kdbOutputLog( - "[DATASOURCE] User cancelled the old DS files import.", - "INFO", + showMessage( + "User cancelled the old DS files import.", + MessageKind.DEBUG, + { logger }, ); return false; }); @@ -429,12 +431,10 @@ export async function importOldDSFiles() { }, ); } else { - window.showInformationMessage( + showMessage( "No old Datasource files found on your VSCODE.", - ); - kdbOutputLog( - "[DATASOURCE] No old Datasource files found on your VSCODE.", - "INFO", + MessageKind.INFO, + { logger }, ); } } diff --git a/src/extension.ts b/src/extension.ts index 2ef191cc..02fbde11 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -113,10 +113,10 @@ import { getServers, hasWorkspaceOrShowOption, initializeLocalServers, - kdbOutputLog, } from "./utils/core"; import { runQFileTerminal } from "./utils/execution"; import { handleFeedbackSurvey } from "./utils/feedbackSurveyUtils"; +import { MessageKind, showMessage } from "./utils/notifications"; import AuthSettings from "./utils/secretStorage"; import { Telemetry } from "./utils/telemetryClient"; import { @@ -126,6 +126,8 @@ import { setUriContent, } from "./utils/workspace"; +const logger = "extension"; + let client: LanguageClient; export async function activate(context: vscode.ExtensionContext) { @@ -196,13 +198,11 @@ export async function activate(context: vscode.ExtensionContext) { vscode.commands.executeCommand("kdb-results.focus"); - kdbOutputLog("kdb extension is now active!", "INFO"); - try { // check for installed q runtime await checkLocalInstall(true); } catch (err) { - vscode.window.showErrorMessage(`${err}`); + showMessage(`${err}`, MessageKind.DEBUG, { logger }); } registerAllExtensionCommands(); @@ -349,6 +349,8 @@ export async function activate(context: vscode.ExtensionContext) { } } handleFeedbackSurvey(); + + showMessage("kdb extension is now active.", MessageKind.DEBUG, { logger }); } function registerHelpCommands(): CommandRegistration[] { @@ -668,7 +670,7 @@ function registerConnectionsCommands(): CommandRegistration[] { true, ); } else { - kdbOutputLog("Connection label not found", "ERROR"); + showMessage("Connection label not found", MessageKind.ERROR); } }, }, @@ -885,7 +887,9 @@ function registerExecuteCommands(): CommandRegistration[] { ); runQFileTerminal(`"${uri.fsPath}"`); } catch (error) { - kdbOutputLog(`Unable to write temp file: ${error}`, "ERROR"); + showMessage(`Unable to write temp file.`, MessageKind.ERROR, { + params: [error], + }); } } }, diff --git a/src/panels/newConnection.ts b/src/panels/newConnection.ts index e73f46f3..56ea3a21 100644 --- a/src/panels/newConnection.ts +++ b/src/panels/newConnection.ts @@ -20,6 +20,7 @@ import { InsightsNode, KdbNode } from "../services/kdbTreeProvider"; import { retrieveConnLabelsNames } from "../utils/connLabel"; import { getNonce } from "../utils/getNonce"; import { getUri } from "../utils/getUri"; +import { MessageKind, showMessage } from "../utils/notifications"; export class NewConnectionPannel { public static currentPanel: NewConnectionPannel | undefined; @@ -96,8 +97,9 @@ export class NewConnectionPannel { this._panel.webview.onDidReceiveMessage((message) => { if (message.command === "kdb.connections.add.bundleq") { if (ext.isBundleQCreated) { - vscode.window.showErrorMessage( + showMessage( "Bundled Q is already created, please remove it first", + MessageKind.ERROR, ); } else { vscode.commands.executeCommand( diff --git a/src/services/connectionManagerService.ts b/src/services/connectionManagerService.ts index 3488b2b0..60e2bc84 100644 --- a/src/services/connectionManagerService.ts +++ b/src/services/connectionManagerService.ts @@ -11,7 +11,7 @@ * specific language governing permissions and limitations under the License. */ -import { window, commands } from "vscode"; +import { commands } from "vscode"; import { LocalConnection } from "../classes/localConnection"; import { ext } from "../extensionVariables"; @@ -31,15 +31,17 @@ import { getKeyForServerName, getServerName, getServers, - kdbOutputLog, removeLocalConnectionContext, updateInsights, updateServers, } from "../utils/core"; import { refreshDataSourcesPanel } from "../utils/dataSource"; +import { MessageKind, showMessage } from "../utils/notifications"; import { sanitizeQuery } from "../utils/queryUtils"; import { Telemetry } from "../utils/telemetryClient"; +const logger = "connectionManagerService"; + export class ConnectionManagementService { public retrieveConnection( connLabel: string, @@ -144,14 +146,18 @@ export class ConnectionManagementService { ); await localConnection.connect((err, conn) => { if (err) { - window.showErrorMessage(err.message); + showMessage(`Connection failed to: ${connLabel}`, MessageKind.ERROR, { + logger, + params: [err], + }); this.isNotConnectedBehaviour(connLabel); return; } if (conn) { - kdbOutputLog( + showMessage( `Connection established successfully to: ${connLabel}`, - "CONNECTION", + MessageKind.DEBUG, + { logger }, ); Telemetry.sendEvent("Connection.Connected.QProcess"); @@ -170,13 +176,15 @@ export class ConnectionManagementService { await insightsConn.connect(); if (insightsConn.connected) { Telemetry.sendEvent("Connection.Connected.Insights"); - kdbOutputLog( + showMessage( `Connection established successfully to: ${connLabel}`, - "CONNECTION", + MessageKind.DEBUG, + { logger }, ); - kdbOutputLog( + showMessage( `${connLabel} connection insights version: ${insightsConn.insightsVersion}`, - "CONNECTION", + MessageKind.DEBUG, + { logger }, ); ext.connectedConnectionList.push(insightsConn); this.isConnectedBehaviour(connection); @@ -288,7 +296,7 @@ export class ConnectionManagementService { } public isNotConnectedBehaviour(connLabel: string): void { - window.showErrorMessage(`Connection failed to: ${connLabel}`); + showMessage(`Connection failed to: ${connLabel}`, MessageKind.ERROR); Telemetry.sendEvent("Connection.Failed"); } @@ -316,9 +324,10 @@ export class ConnectionManagementService { commands.executeCommand("setContext", "kdb.pythonEnabled", false); } Telemetry.sendEvent("Connection.Disconnected." + connType); - kdbOutputLog( - `[CONNECTION] Connection closed: ${connection.connLabel}`, - "INFO", + showMessage( + `Connection closed: ${connection.connLabel}`, + MessageKind.DEBUG, + { logger }, ); ext.serverProvider.reload(); } @@ -368,9 +377,10 @@ export class ConnectionManagementService { if (retrievedConn instanceof InsightsConnection) { conn = retrievedConn; } else { - kdbOutputLog( - "[RESET SCRATCHPAD] Please connect to an Insights connection to use this feature.", - "ERROR", + showMessage( + "Please connect to an Insights connection to use this feature.", + MessageKind.ERROR, + { logger }, ); return; } @@ -380,9 +390,10 @@ export class ConnectionManagementService { !ext.activeConnection || !(ext.activeConnection instanceof InsightsConnection) ) { - kdbOutputLog( - "[RESET SCRATCHPAD] Please activate an Insights connection to use this feature.", - "ERROR", + showMessage( + "Please activate an Insights connection to use this feature.", + MessageKind.ERROR, + { logger }, ); return; } @@ -394,8 +405,10 @@ export class ConnectionManagementService { isBaseVersionGreaterOrEqual(conn.insightsVersion, 1.13) ) { const confirmationPrompt = `Reset Scratchpad? All data in the ${conn.connLabel} Scratchpad will be lost, and variables will be reset.`; - const selection = await window.showInformationMessage( + const selection = await showMessage( confirmationPrompt, + MessageKind.INFO, + {}, "Yes", "No", ); @@ -403,16 +416,18 @@ export class ConnectionManagementService { if (selection === "Yes") { await conn.resetScratchpad(); } else { - kdbOutputLog( - "[RESET SCRATCHPAD] The user canceled the scratchpad reset.", - "INFO", + showMessage( + "The user canceled the scratchpad reset.", + MessageKind.DEBUG, + { logger }, ); return; } } else { - kdbOutputLog( - "[RESET SCRATCHPAD] Please connect to an Insights connection with version 1.13 or higher.", - "ERROR", + showMessage( + "Please connect to an Insights connection with version 1.13 or higher.", + MessageKind.ERROR, + { logger }, ); } } @@ -445,24 +460,27 @@ export class ConnectionManagementService { ): string { const metaType = this.getMetaInfoType(metaTypeString.toUpperCase()); if (!metaType) { - kdbOutputLog( - "[META] The meta info type that you try to open is not valid", - "ERROR", + showMessage( + "The meta info type that you try to open is not valid", + MessageKind.ERROR, + { logger }, ); return ""; } const connection = this.retrieveConnectedConnection(connLabel); if (!connection) { - kdbOutputLog( - "[META] The connection that you try to open meta info is not connected", - "ERROR", + showMessage( + "The connection that you try to open meta info is not connected", + MessageKind.ERROR, + { logger }, ); return ""; } if (connection instanceof LocalConnection) { - kdbOutputLog( - "[META] The connection that you try to open meta info is not an Insights connection", - "ERROR", + showMessage( + "The connection that you try to open meta info is not an Insights connection", + MessageKind.ERROR, + { logger }, ); return ""; } diff --git a/src/services/dataSourceEditorProvider.ts b/src/services/dataSourceEditorProvider.ts index 4510ca18..63512850 100644 --- a/src/services/dataSourceEditorProvider.ts +++ b/src/services/dataSourceEditorProvider.ts @@ -17,7 +17,6 @@ import { CustomTextEditorProvider, Disposable, ExtensionContext, - ProgressLocation, Range, TextDocument, Webview, @@ -43,9 +42,10 @@ import { import { DataSourceCommand, DataSourceMessage2 } from "../models/messages"; import { MetaObjectPayload } from "../models/meta"; import { UDA } from "../models/uda"; -import { kdbOutputLog, offerConnectAction } from "../utils/core"; +import { offerConnectAction } from "../utils/core"; import { getNonce } from "../utils/getNonce"; import { getUri } from "../utils/getUri"; +import { MessageKind, Runner, showMessage } from "../utils/notifications"; import { parseUDAList } from "../utils/uda"; export class DataSourceEditorProvider implements CustomTextEditorProvider { @@ -92,16 +92,12 @@ export class DataSourceEditorProvider implements CustomTextEditorProvider { this.cache.set(connLabel, meta); } catch { - window.showErrorMessage( + showMessage( "No database running in this Insights connection.", + MessageKind.ERROR, ); meta = Promise.resolve({}); this.cache.set(connLabel, meta); - kdbOutputLog( - "No database running in this Insights connection.", - "ERROR", - true, - ); } return (await meta) || Promise.resolve({}); } @@ -193,22 +189,17 @@ export class DataSourceEditorProvider implements CustomTextEditorProvider { } case DataSourceCommand.Refresh: { const selectedServer = getServerForUri(document.uri) || ""; - if (!connMngService.isConnected(selectedServer)) { - offerConnectAction(selectedServer); - break; - } - await window.withProgress( - { - cancellable: false, - location: ProgressLocation.Notification, - title: "Refreshing meta data...", - }, - async () => { + if (connMngService.isConnected(selectedServer)) { + const runner = Runner.create(async () => { await connMngService.refreshGetMeta(selectedServer); this.cache.delete(selectedServer); updateWebview(); - }, - ); + }); + runner.title = "Refreshing meta data"; + await runner.execute(); + } else { + offerConnectAction(selectedServer); + } break; } case DataSourceCommand.Run: { diff --git a/src/services/notebookController.ts b/src/services/notebookController.ts index d2534c42..908f7823 100644 --- a/src/services/notebookController.ts +++ b/src/services/notebookController.ts @@ -16,10 +16,12 @@ import * as vscode from "vscode"; import { InsightsConnection } from "../classes/insightsConnection"; import { ext } from "../extensionVariables"; import { ConnectionManagementService } from "../services/connectionManagerService"; -import { kdbOutputLog } from "../utils/core"; +import { MessageKind, showMessage, timeout } from "../utils/notifications"; import { resultToBase64 } from "../utils/queryUtils"; import { convertToGrid, formatResult } from "../utils/resultsRenderer"; +const logger = "notebookController"; + export class KxNotebookController { readonly controllerId = "kx-notebook-1"; readonly notebookType = "kx-notebook"; @@ -55,8 +57,10 @@ export class KxNotebookController { ): Promise { const conn = ext.activeConnection; if (conn === undefined) { - vscode.window.showErrorMessage( + showMessage( "You aren't connected to any connection. Once connected please try again.", + MessageKind.ERROR, + { logger }, ); return; } @@ -71,13 +75,25 @@ export class KxNotebookController { execution.start(Date.now()); try { - const results = await manager.executeQuery( - cell.document.getText(), - conn.connLabel, - ".", - false, - isPython, - ); + const results = await Promise.race([ + await manager.executeQuery( + cell.document.getText(), + conn.connLabel, + ".", + false, + isPython, + ), + new Promise((_, reject) => { + const updateCancelled = () => { + if (execution.token.isCancellationRequested) { + reject(new vscode.CancellationError()); + } + }; + updateCancelled(); + execution.token.onCancellationRequested(updateCancelled); + }), + timeout(), + ]); const rendered = render(results, isPython, isInsights, connVersion); @@ -87,7 +103,10 @@ export class KxNotebookController { ]), ]); } catch (error) { - kdbOutputLog(`${error}`, "ERROR"); + showMessage("Unable run code blocck.", MessageKind.ERROR, { + logger, + params: [error], + }); } finally { execution.end(true, Date.now()); } diff --git a/src/services/resultsPanelProvider.ts b/src/services/resultsPanelProvider.ts index 5746983f..e717697b 100644 --- a/src/services/resultsPanelProvider.ts +++ b/src/services/resultsPanelProvider.ts @@ -22,12 +22,14 @@ import { } from "vscode"; import { ext } from "../extensionVariables"; -import { kdbOutputLog } from "../utils/core"; import * as utils from "../utils/execution"; import { getNonce } from "../utils/getNonce"; import { getUri } from "../utils/getUri"; +import { MessageKind, showMessage } from "../utils/notifications"; import { convertToGrid, formatResult } from "../utils/resultsRenderer"; +const logger = "resultsPanelProvider"; + export class KdbResultsViewProvider implements WebviewViewProvider { public static readonly viewType = "kdb-results"; public isInsights = false; @@ -101,12 +103,12 @@ export class KdbResultsViewProvider implements WebviewViewProvider { exportToCsv() { if (ext.resultPanelCSV === "") { - window.showErrorMessage("No results to export"); + showMessage("No results to export", MessageKind.ERROR); return; } const workspaceFolders = workspace.workspaceFolders; if (!workspaceFolders) { - window.showErrorMessage("Open a folder to export results"); + showMessage("Open a folder to export results", MessageKind.ERROR); return; } const workspaceUri = workspaceFolders[0].uri; @@ -137,7 +139,9 @@ export class KdbResultsViewProvider implements WebviewViewProvider { let gridOptions = undefined; if (!this._view) { - kdbOutputLog("[Results Tab] No view to update", "ERROR"); + showMessage("No view to update", MessageKind.ERROR, { + logger, + }); return; } diff --git a/src/utils/connLabel.ts b/src/utils/connLabel.ts index 43851c39..efa48edd 100644 --- a/src/utils/connLabel.ts +++ b/src/utils/connLabel.ts @@ -14,11 +14,13 @@ import { workspace } from "vscode"; import { ext } from "../extensionVariables"; -import { kdbOutputLog } from "./core"; +import { MessageKind, showMessage } from "./notifications"; import { ConnectionLabel, Labels } from "../models/labels"; import { NewConnectionPannel } from "../panels/newConnection"; import { InsightsNode, KdbNode } from "../services/kdbTreeProvider"; +const logger = "connLabel"; + export function getWorkspaceLabels() { const existingConnLbls = workspace .getConfiguration() @@ -37,7 +39,7 @@ export function createNewLabel(name: string, colorName: string) { (color) => color.name.toLowerCase() === colorName.toLowerCase(), ); if (name === "") { - kdbOutputLog("Label name can't be empty", "ERROR"); + showMessage("Label name can't be empty.", MessageKind.ERROR, { logger }); } if (color && name !== "") { const newLbl: Labels = { @@ -49,7 +51,9 @@ export function createNewLabel(name: string, colorName: string) { .getConfiguration() .update("kdb.connectionLabels", ext.connLabelList, true); } else { - kdbOutputLog("No Color selected for the label", "ERROR"); + showMessage("No Color selected for the label.", MessageKind.ERROR, { + logger, + }); } } diff --git a/src/utils/core.ts b/src/utils/core.ts index ceb75f1c..c0276e25 100644 --- a/src/utils/core.ts +++ b/src/utils/core.ts @@ -19,11 +19,12 @@ import { env } from "node:process"; import { tmpdir } from "os"; import { join } from "path"; import * as semver from "semver"; -import { commands, ConfigurationTarget, Uri, window, workspace } from "vscode"; +import { commands, ConfigurationTarget, Uri, workspace } from "vscode"; import { installTools } from "../commands/installTools"; import { ext } from "../extensionVariables"; import { tryExecuteCommand } from "./cpUtils"; +import { MessageKind, showMessage } from "./notifications"; import { showRegistrationNotification } from "./registration"; import { Telemetry } from "./telemetryClient"; import { @@ -34,8 +35,12 @@ import { } from "../models/connectionsModels"; import { QueryResult } from "../models/queryResult"; +const logger = "core"; + export function log(childProcess: ChildProcess): void { - kdbOutputLog(`Process ${childProcess.pid} started`, "INFO"); + showMessage(`Process ${childProcess.pid} started`, MessageKind.DEBUG, { + logger, + }); } export async function checkOpenSslInstalled(): Promise { @@ -50,9 +55,10 @@ export async function checkOpenSslInstalled(): Promise { const matcher = /(\d+.\d+.\d+)/; const installedVersion = result.cmdOutput.match(matcher); - kdbOutputLog( + showMessage( `Detected version ${installedVersion} of OpenSSL installed.`, - "INFO", + MessageKind.DEBUG, + { logger }, ); return semver.clean(installedVersion ? installedVersion[1] : ""); @@ -151,7 +157,11 @@ export function saveLocalProcessObj( childProcess: ChildProcess, args: string[], ): void { - kdbOutputLog(`Child process id ${childProcess.pid} saved in cache.`, "INFO"); + showMessage( + `Child process id ${childProcess.pid} saved in cache.`, + MessageKind.DEBUG, + { logger }, + ); ext.localProcessObjects[args[2]] = childProcess; } @@ -314,75 +324,61 @@ export function getServerAlias(serverList: ServerDetails[]): void { }); } -export function kdbOutputLog( - message: string, - type: string, - supressDialog?: boolean, -): void { - const dateNow = new Date().toLocaleDateString(); - const timeNow = new Date().toLocaleTimeString(); - ext.outputChannel.appendLine(`[${dateNow} ${timeNow}] [${type}] ${message}`); - if (type === "ERROR" && !supressDialog) { - window.showErrorMessage( - `Error occured, check kdb output channel for details.`, - ); - } -} - export function tokenUndefinedError(connLabel: string): void { - kdbOutputLog( + showMessage( `Error retrieving access token for Insights connection named: ${connLabel}`, - "ERROR", + MessageKind.ERROR, ); } export function invalidUsernameJWT(connLabel: string): void { - kdbOutputLog( + showMessage( `JWT did not contain a valid preferred username for Insights connection: ${connLabel}`, - "ERROR", + MessageKind.ERROR, ); } /* istanbul ignore next */ export function offerConnectAction(connLabel: string): void { - window - .showInformationMessage( - `You aren't connected to ${connLabel}, would you like to connect? Once connected please try again.`, - "Connect", - "Cancel", - ) - .then(async (result) => { - if (result === "Connect") { - await commands.executeCommand( - "kdb.connections.connect.via.dialog", - connLabel, - ); - } - }); + showMessage( + `You aren't connected to ${connLabel}, would you like to connect? Once connected please try again.`, + MessageKind.INFO, + {}, + "Connect", + "Cancel", + ).then(async (result) => { + if (result === "Connect") { + await commands.executeCommand( + "kdb.connections.connect.via.dialog", + connLabel, + ); + } + }); } export function noSelectedConnectionAction(): void { - window.showInformationMessage( + showMessage( `You didn't selected any existing connection to execute this action, please select a connection and try again.`, + MessageKind.INFO, ); } /* istanbul ignore next */ export function offerReconnectionAfterEdit(connLabel: string): void { - window - .showInformationMessage( - `You are no longer connected to ${connLabel}, would you like to connect?`, - "Connect", - "Cancel", - ) - .then(async (result) => { - if (result === "Connect") { - await commands.executeCommand( - "kdb.connections.connect.via.dialog", - connLabel, - ); - } - }); + showMessage( + `You are no longer connected to ${connLabel}, would you like to connect?`, + MessageKind.INFO, + {}, + "Connect", + "Cancel", + ).then(async (result) => { + if (result === "Connect") { + await commands.executeCommand( + "kdb.connections.connect.via.dialog", + connLabel, + ); + } + }); } export function getInsightsAlias(insightsList: InsightDetails[]): void { @@ -441,7 +437,7 @@ export async function checkLocalInstall( if (QHOME || env.QHOME) { env.QHOME = QHOME || env.QHOME; if (!pathExists(env.QHOME!)) { - kdbOutputLog("QHOME path stored is empty", "ERROR"); + showMessage("QHOME path stored is empty.", MessageKind.ERROR); } await writeFile( join(__dirname, "qinstall.md"), @@ -453,7 +449,11 @@ export async function checkLocalInstall( .getConfiguration() .update("kdb.qHomeDirectory", env.QHOME, ConfigurationTarget.Global); - kdbOutputLog(`Installation of q found here: ${env.QHOME}`, "INFO"); + showMessage( + `Installation of q found here: ${env.QHOME}`, + MessageKind.DEBUG, + { logger }, + ); showRegistrationNotification(); @@ -461,8 +461,9 @@ export async function checkLocalInstall( .getConfiguration() .get("kdb.hideInstallationNotification"); if (!hideNotification) { - window.showInformationMessage( + showMessage( `Installation of q found here: ${env.QHOME}`, + MessageKind.INFO, ); } @@ -481,28 +482,24 @@ export async function checkLocalInstall( // set custom context that QHOME is not setup to control walkthrough visibility commands.executeCommand("setContext", "kdb.showInstallWalkthrough", true); - window - .showInformationMessage( - "Local q installation not found!", - "Install new instance", - "No", - "Never show again", - ) - .then(async (installResult) => { - if (installResult === "Install new instance") { - await installTools(); - } else if (installResult === "Never show again") { - await workspace - .getConfiguration() - .update( - "kdb.neverShowQInstallAgain", - true, - ConfigurationTarget.Global, - ); - } else { - showRegistrationNotification(); - } - }); + showMessage( + "Local q installation not found!", + MessageKind.INFO, + {}, + "Install new instance", + "No", + "Never show again", + ).then(async (installResult) => { + if (installResult === "Install new instance") { + await installTools(); + } else if (installResult === "Never show again") { + await workspace + .getConfiguration() + .update("kdb.neverShowQInstallAgain", true, ConfigurationTarget.Global); + } else { + showRegistrationNotification(); + } + }); } export async function convertBase64License( @@ -644,16 +641,16 @@ export function hasWorkspaceOrShowOption(action: string) { if (workspace.workspaceFolders && workspace.workspaceFolders.length > 0) { return true; } - window - .showWarningMessage( - `No workspace folder is open. Please open a folder to enable ${action}.`, - "Open", - ) - .then((res) => { - if (res === "Open") { - commands.executeCommand("workbench.action.files.openFolder"); - } - }); + showMessage( + `No workspace folder is open. Please open a folder to enable ${action}.`, + MessageKind.WARNING, + {}, + "Open", + ).then((res) => { + if (res === "Open") { + commands.executeCommand("workbench.action.files.openFolder"); + } + }); return false; } diff --git a/src/utils/cpUtils.ts b/src/utils/cpUtils.ts index 117e9fe7..e17ecdf8 100644 --- a/src/utils/cpUtils.ts +++ b/src/utils/cpUtils.ts @@ -16,7 +16,9 @@ import * as os from "os"; import { join } from "path"; import { ext } from "../extensionVariables"; -import { kdbOutputLog } from "./core"; +import { MessageKind, showMessage } from "./notifications"; + +const logger = "cpUtils"; export async function executeCommand( workingDirectory: string | undefined, @@ -36,9 +38,10 @@ export async function executeCommand( `Failed to run ${command} command. Check output window for more details.`, ); } else { - kdbOutputLog( + showMessage( `Finished running command: ${command} ${result.formattedArgs}`, - "INFO", + MessageKind.DEBUG, + { logger }, ); } return result.cmdOutput; @@ -89,17 +92,17 @@ export async function tryExecuteCommand( data = data.toString(); cmdOutput = cmdOutput.concat(data); cmdOutputIncludingStderr = cmdOutputIncludingStderr.concat(data); - kdbOutputLog(data, "INFO"); + showMessage(data, MessageKind.DEBUG, { logger }); }); childProc.stderr?.on("data", (data: string | Buffer) => { data = data.toString(); cmdOutputIncludingStderr = cmdOutputIncludingStderr.concat(data); - kdbOutputLog(data, "INFO"); + showMessage(data, MessageKind.DEBUG, { logger }); }); childProc.on("error", (error) => { - kdbOutputLog(error.message, "ERROR"); + showMessage(error.message, MessageKind.ERROR); reject(error); }); diff --git a/src/utils/dataSource.ts b/src/utils/dataSource.ts index 7e4e5f29..2d20ca15 100644 --- a/src/utils/dataSource.ts +++ b/src/utils/dataSource.ts @@ -13,29 +13,33 @@ import * as fs from "fs"; import path from "path"; -import { workspace, window, Uri } from "vscode"; +import { workspace, Uri } from "vscode"; import { InsightsConnection } from "../classes/insightsConnection"; import { ext } from "../extensionVariables"; -import { kdbOutputLog } from "./core"; +import { MessageKind, showMessage } from "./notifications"; import { Telemetry } from "./telemetryClient"; import { DataSourceFiles } from "../models/dataSource"; import { DataSourcesPanel } from "../panels/datasource"; +const logger = "dataSource"; + export function createKdbDataSourcesFolder(): string { const rootPath = ext.context.globalStorageUri.fsPath; const kdbDataSourcesFolderPath = path.join(rootPath, ext.kdbDataSourceFolder); if (!fs.existsSync(rootPath)) { - kdbOutputLog( - `[DATSOURCE] Directory created to the extension folder: ${rootPath}`, - "INFO", + showMessage( + `Directory created to the extension folder: ${rootPath}`, + MessageKind.DEBUG, + { logger }, ); fs.mkdirSync(rootPath); } if (!fs.existsSync(kdbDataSourcesFolderPath)) { - kdbOutputLog( - `[DATSOURCE] Directory created to the extension folder: ${kdbDataSourcesFolderPath}`, - "INFO", + showMessage( + `Directory created to the extension folder: ${kdbDataSourcesFolderPath}`, + MessageKind.DEBUG, + { logger }, ); fs.mkdirSync(kdbDataSourcesFolderPath); } @@ -51,12 +55,10 @@ export function convertTimeToTimestamp(time: string): string { const timePart = parts[1].replace("Z", "0").padEnd(9, "0"); return `${datePart}.${timePart}`; } catch (error) { - kdbOutputLog( - `The string param is in an incorrect format. Param: ${time} Error: ${error}`, - "ERROR", - ); - console.error( - `The string param is in an incorrect format. Param: ${time} Error: ${error}`, + showMessage( + "The string param is in an incorrect format.", + MessageKind.ERROR, + { logger, params: [time, error] }, ); return ""; } @@ -151,7 +153,7 @@ export async function addDSToLocalFolder(ds: DataSourceFiles): Promise { filePath = path.join(importToUri.fsPath, fileName); } fs.writeFileSync(filePath, JSON.stringify(ds)); - window.showInformationMessage(`Datasource created.`); + showMessage(`Datasource created.`, MessageKind.INFO); Telemetry.sendEvent("Datasource.Created"); } } diff --git a/src/utils/execution.ts b/src/utils/execution.ts index 205f2e97..cc642d7f 100644 --- a/src/utils/execution.ts +++ b/src/utils/execution.ts @@ -16,9 +16,11 @@ import path from "path"; import { Uri, window, workspace } from "vscode"; import { ext } from "../extensionVariables"; -import { kdbOutputLog } from "./core"; +import { MessageKind, showMessage } from "./notifications"; import { QueryResultType } from "../models/queryResult"; +const logger = "execution"; + interface tblHeader { label: string; count: number; @@ -130,12 +132,15 @@ export async function exportToCsv(workspaceUri: Uri): Promise { try { await workspace.fs.writeFile(filePath, Buffer.from(ext.resultPanelCSV)); - kdbOutputLog("file located at: " + filePath.fsPath, "INFO"); + showMessage("file located at: " + filePath.fsPath, MessageKind.DEBUG, { + logger, + }); window.showTextDocument(filePath, { preview: false }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); - kdbOutputLog(`Error writing file: ${errorMessage}`, "ERROR"); - window.showErrorMessage(`Failed to write file: ${errorMessage}`); + showMessage(`Failed to write file: ${errorMessage}`, MessageKind.ERROR, { + logger, + }); } } diff --git a/src/utils/executionConsole.ts b/src/utils/executionConsole.ts index f821cf7a..00669442 100644 --- a/src/utils/executionConsole.ts +++ b/src/utils/executionConsole.ts @@ -18,6 +18,7 @@ import { getHideDetailedConsoleQueryOutput, setOutputWordWrapper, } from "./core"; +import { MessageKind, showMessage } from "./notifications"; import { addQueryHistory, checkIfIsDatasource, @@ -180,7 +181,10 @@ export class ExecutionConsole { ); } } else { - window.showErrorMessage(`Please connect to a KDB or Insights server`); + showMessage( + `Please connect to a KDB or Insights server`, + MessageKind.ERROR, + ); this._console.appendLine(`Please connect to a KDB or Insights server`); commands.executeCommand("kdb.connections.disconnect"); addQueryHistory( diff --git a/src/utils/feedbackSurveyUtils.ts b/src/utils/feedbackSurveyUtils.ts index 6490f8c6..fe62194a 100644 --- a/src/utils/feedbackSurveyUtils.ts +++ b/src/utils/feedbackSurveyUtils.ts @@ -14,6 +14,7 @@ import * as vscode from "vscode"; import { ext } from "../extensionVariables"; +import { MessageKind, showMessage } from "./notifications"; export async function feedbackSurveyDialog( sawSurveyAlready: boolean, @@ -47,8 +48,10 @@ export async function feedbackSurveyDialog( async function showSurveyDialog() { const SURVEY_URL = ext.urlLinks.survey; - const result = await vscode.window.showInformationMessage( + const result = await showMessage( "Got 2 Minutes? Help us make the KX extension even better for your workflows.", + MessageKind.INFO, + {}, "Take Survey", "Don't show me this message next time", ); diff --git a/src/utils/loggers.ts b/src/utils/loggers.ts new file mode 100644 index 00000000..54310a10 --- /dev/null +++ b/src/utils/loggers.ts @@ -0,0 +1,31 @@ +/* + * Copyright (c) 1998-2025 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"; + +import { ext } from "../extensionVariables"; + +export function kdbOutputLog( + message: string, + type: string, + supressDialog?: boolean, +): void { + const dateNow = new Date().toLocaleDateString(); + const timeNow = new Date().toLocaleTimeString(); + ext.outputChannel.appendLine(`[${dateNow} ${timeNow}] [${type}] ${message}`); + if (type === "ERROR" && !supressDialog) { + vscode.window.showErrorMessage( + `Error occured, check kdb output channel for details.`, + ); + } +} diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts new file mode 100644 index 00000000..9fa84295 --- /dev/null +++ b/src/utils/notifications.ts @@ -0,0 +1,131 @@ +/* + * Copyright (c) 1998-2025 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"; + +import { kdbOutputLog } from "./loggers"; + +const logger = "notifications"; + +export const enum Cancellable { + NONE = 0, + EXECUTOR = 1, + RUNNER = 2, +} + +export type Executor = ( + progress: vscode.Progress<{ + message?: string; + increment?: number; + }>, + token: vscode.CancellationToken, +) => Promise; + +export class Runner { + public title = ""; + public location = vscode.ProgressLocation.Window; + public cancellable = Cancellable.RUNNER; + protected _cancelled = false; + private constructor(protected readonly executor: Executor) {} + + public get cancelled(): boolean { + return this._cancelled; + } + + public execute() { + return vscode.window.withProgress( + { + title: this.title, + location: this.location, + cancellable: this.cancellable !== Cancellable.NONE, + }, + (progress, token) => { + return this.cancellable === Cancellable.RUNNER + ? Promise.race([ + this.executor(progress, token), + new Promise((_, reject) => { + const updateCancelled = () => { + this._cancelled = token.isCancellationRequested; + if (this._cancelled) { + showMessage(`${this.title} cancelled.`, MessageKind.DEBUG, { + logger, + }); + reject(new vscode.CancellationError()); + } + }; + token.onCancellationRequested(updateCancelled); + updateCancelled(); + }), + timeout(), + ]) + : this.executor(progress, token); + }, + ); + } + + public static create(executor: Executor): Runner { + return new Runner(executor); + } +} + +export function sleep(ms: number): Promise { + return new Promise((resolve, _) => setTimeout(() => resolve(), ms)); +} + +export function timeout(ms = 1000 * 60 * 5): Promise { + return new Promise((_, reject) => + setTimeout(() => reject(new vscode.CancellationError()), ms), + ); +} + +export const enum MessageKind { + DEBUG = "DEBUG", + INFO = "INFO", + WARNING = "WARNING", + ERROR = "ERROR", +} + +export function showMessage( + message: string, + kind: MessageKind, + options: { logger?: string; params?: Array } = {}, + ...items: T[] +): Thenable { + if (options.logger) { + const params = getParams(options.params); + const log = `[${options.logger}] ${message} ${params}`.trim(); + kdbOutputLog(log, kind, true); + } + switch (kind) { + case MessageKind.ERROR: + return vscode.window.showErrorMessage(message, ...items); + case MessageKind.WARNING: + return vscode.window.showWarningMessage(message, ...items); + case MessageKind.INFO: + return vscode.window.showInformationMessage(message, ...items); + default: + return Promise.resolve(undefined); + } +} + +function getParams(params?: Array) { + if (params) { + try { + return JSON.stringify(params); + } catch (error) { + return JSON.stringify(error); + } + } else { + return ""; + } +} diff --git a/src/utils/queryUtils.ts b/src/utils/queryUtils.ts index 12ea038b..fe8a9484 100644 --- a/src/utils/queryUtils.ts +++ b/src/utils/queryUtils.ts @@ -15,7 +15,8 @@ import { readFileSync } from "fs"; import { join } from "path"; import { ext } from "../extensionVariables"; -import { isBaseVersionGreaterOrEqual, kdbOutputLog } from "./core"; +import { isBaseVersionGreaterOrEqual } from "./core"; +import { MessageKind, showMessage } from "./notifications"; import { normalizeAssemblyTarget } from "./shared"; import { DCDS, deserialize, isCompressed, uncompress } from "../ipc/c"; import { DDateClass, DDateTimeClass, DTimestampClass } from "../ipc/cClasses"; @@ -26,6 +27,8 @@ import { DataSourceFiles, DataSourceTypes } from "../models/dataSource"; import { QueryHistory } from "../models/queryHistory"; import { ScratchpadStacktrace } from "../models/scratchpadResult"; +const logger = "queryUtils"; + export function sanitizeQuery(query: string): string { if (query[0] === "`") { query = query + " "; @@ -96,7 +99,7 @@ export function handleWSError(ab: ArrayBuffer): any { } } - kdbOutputLog(`Error : ${errorString}`, "ERROR", true); + showMessage(`Error : ${errorString}`, MessageKind.DEBUG, { logger }); return { error: errorString }; } diff --git a/src/utils/registration.ts b/src/utils/registration.ts index f50d532a..0c2c3820 100644 --- a/src/utils/registration.ts +++ b/src/utils/registration.ts @@ -11,9 +11,10 @@ * specific language governing permissions and limitations under the License. */ -import { ConfigurationTarget, window, workspace } from "vscode"; +import { ConfigurationTarget, workspace } from "vscode"; import { ext } from "../extensionVariables"; +import { MessageKind, showMessage } from "./notifications"; import { openUrl } from "./openUrl"; export function showRegistrationNotification(): void { @@ -21,13 +22,17 @@ export function showRegistrationNotification(): void { .getConfiguration() .get("kdb.hideSubscribeRegistrationNotification"); if (setting !== undefined && setting === false) { - window - .showInformationMessage("Subscribe to updates", "Opt-In", "Ignore") - .then((result) => { - if (result === "Opt-In") { - openUrl(ext.kdbNewsletterUrl); - } - }); + showMessage( + "Subscribe to updates", + MessageKind.INFO, + {}, + "Opt-In", + "Ignore", + ).then((result) => { + if (result === "Opt-In") { + openUrl(ext.kdbNewsletterUrl); + } + }); } // hide notification for future extension use @@ -36,6 +41,6 @@ export function showRegistrationNotification(): void { .update( "kdb.hideSubscribeRegistrationNotification", true, - ConfigurationTarget.Global + ConfigurationTarget.Global, ); } diff --git a/src/utils/shell.ts b/src/utils/shell.ts index da7295bc..56b4be7d 100644 --- a/src/utils/shell.ts +++ b/src/utils/shell.ts @@ -13,13 +13,17 @@ import { ChildProcess } from "node:child_process"; -import { kdbOutputLog } from "./core"; import { ICommandResult, tryExecuteCommand } from "./cpUtils"; +import { MessageKind, showMessage } from "./notifications"; + +const logger = "shell"; const isWin = process.platform === "win32"; export function log(childProcess: ChildProcess): void { - kdbOutputLog(`Process ${childProcess.pid} killed`, "INFO"); + showMessage(`Process ${childProcess.pid} killed`, MessageKind.DEBUG, { + logger, + }); } export async function killPid(pid = NaN): Promise { @@ -33,7 +37,9 @@ export async function killPid(pid = NaN): Promise { } else if (process.platform === "darwin") { result = await tryExecuteCommand("/bin", killPidCommand(pid), log); } - kdbOutputLog(`Destroying q process result: ${result}`, "INFO"); + showMessage(`Destroying q process result: ${result}`, MessageKind.DEBUG, { + logger, + }); } function killPidCommand(pid: number): string { diff --git a/test/suite/commands.test.ts b/test/suite/commands.test.ts index ab1a4110..51bf0b96 100644 --- a/test/suite/commands.test.ts +++ b/test/suite/commands.test.ts @@ -57,6 +57,7 @@ import * as coreUtils from "../../src/utils/core"; import * as dsUtils from "../../src/utils/dataSource"; import * as dataSourceUtils from "../../src/utils/dataSource"; import { ExecutionConsole } from "../../src/utils/executionConsole"; +import * as loggers from "../../src/utils/loggers"; import * as queryUtils from "../../src/utils/queryUtils"; import { MAX_STR_LEN } from "../../src/validators/kdbValidator"; @@ -1175,7 +1176,7 @@ describe("dataSourceCommand2", () => { let kdbOutputLogStub: sinon.SinonStub; beforeEach(() => { - kdbOutputLogStub = sinon.stub(coreUtils, "kdbOutputLog"); + kdbOutputLogStub = sinon.stub(loggers, "kdbOutputLog"); }); afterEach(() => { sinon.restore(); @@ -1420,7 +1421,7 @@ describe("serverCommand", () => { beforeEach(() => { showOpenDialogStub = sinon.stub(vscode.window, "showOpenDialog"); - kdbOutputLogStub = sinon.stub(coreUtils, "kdbOutputLog"); + kdbOutputLogStub = sinon.stub(loggers, "kdbOutputLog"); _addImportedConnectionsStub = sinon.stub( serverCommand, "addImportedConnections", @@ -1439,8 +1440,9 @@ describe("serverCommand", () => { assert( kdbOutputLogStub.calledWith( - "[IMPORT CONNECTION]No file selected", + "[serverCommand] No file selected.", "ERROR", + true, ), ); }); @@ -1503,7 +1505,7 @@ describe("serverCommand", () => { "addInsightsConnection", ); addKdbConnectionStub = sinon.stub(serverCommand, "addKdbConnection"); - kdbOutputLogStub = sinon.stub(coreUtils, "kdbOutputLog"); + kdbOutputLogStub = sinon.stub(loggers, "kdbOutputLog"); _getInsightsStub = sinon .stub(coreUtils, "getInsights") .returns(undefined); @@ -1563,13 +1565,13 @@ describe("serverCommand", () => { assert( kdbOutputLogStub.calledWith( - "[IMPORT CONNECTION]Connections imported successfully", + "[serverCommand] Connections imported successfully.", "INFO", ), ); assert( showInformationMessageStub.calledWith( - "Connections imported successfully", + "Connections imported successfully.", ), ); }); @@ -2443,7 +2445,7 @@ describe("serverCommand", () => { beforeEach(() => { sandbox = sinon.createSandbox(); - kdbOutputLogStub = sinon.stub(coreUtils, "kdbOutputLog"); + kdbOutputLogStub = sinon.stub(loggers, "kdbOutputLog"); }); afterEach(() => { @@ -2465,7 +2467,7 @@ describe("serverCommand", () => { sinon.assert.calledOnce(kdbOutputLogStub); sinon.assert.calledWith( kdbOutputLogStub, - "[EXPORT CONNECTIONS] No connections found to be exported", + "[serverCommand] No connections found to be exported.", "ERROR", ); @@ -2489,8 +2491,8 @@ describe("serverCommand", () => { sinon.assert.calledOnce(kdbOutputLogStub); sinon.assert.calledWith( kdbOutputLogStub, - "[EXPORT CONNECTIONS] Save operation was cancelled by the user", - "INFO", + "[serverCommand] Save operation was cancelled by the user.", + "DEBUG", ); exportConnectionStub.restore(); @@ -2788,7 +2790,7 @@ describe("workspaceCommand", () => { task({}, token); }); - kdbOutputLogStub = sinon.stub(coreUtils, "kdbOutputLog"); + kdbOutputLogStub = sinon.stub(loggers, "kdbOutputLog"); }); afterEach(() => { sinon.restore(); @@ -2834,8 +2836,8 @@ describe("workspaceCommand", () => { sinon.assert.calledOnce(kdbOutputLogStub); sinon.assert.calledWith( kdbOutputLogStub, - "[DATASOURCE] User cancelled the old DS files import.", - "INFO", + "[workspaceCommand] User cancelled the old DS files import.", + "DEBUG", ); }); }); diff --git a/test/suite/panels.test.ts b/test/suite/panels.test.ts index a3a22abe..a95624f2 100644 --- a/test/suite/panels.test.ts +++ b/test/suite/panels.test.ts @@ -24,8 +24,8 @@ import { DataSourcesPanel } from "../../src/panels/datasource"; import { NewConnectionPannel } from "../../src/panels/newConnection"; import { InsightsNode, KdbNode } from "../../src/services/kdbTreeProvider"; import { KdbResultsViewProvider } from "../../src/services/resultsPanelProvider"; -import * as coreUtils from "../../src/utils/core"; import * as utils from "../../src/utils/execution"; +import * as loggers from "../../src/utils/loggers"; import * as renderer from "../../src/utils/resultsRenderer"; describe("WebPanels", () => { @@ -496,7 +496,7 @@ describe("WebPanels", () => { } as any; postMessageStub = resultsPanel["_view"].webview .postMessage as sinon.SinonStub; - kdbOutputLogStub = sinon.stub(coreUtils, "kdbOutputLog"); + kdbOutputLogStub = sinon.stub(loggers, "kdbOutputLog"); convertToGridStub = sinon.stub(renderer, "convertToGrid"); }); @@ -509,7 +509,7 @@ describe("WebPanels", () => { resultsPanel.updateWebView("test"); sinon.assert.calledWith( kdbOutputLogStub, - "[Results Tab] No view to update", + "[resultsPanelProvider] No view to update", "ERROR", ); }); diff --git a/test/suite/services.test.ts b/test/suite/services.test.ts index 1e6ab64b..c007ca90 100644 --- a/test/suite/services.test.ts +++ b/test/suite/services.test.ts @@ -71,8 +71,8 @@ import { QueryHistoryTreeItem, } from "../../src/services/queryHistoryProvider"; import { WorkspaceTreeProvider } from "../../src/services/workspaceTreeProvider"; -import * as coreUtils from "../../src/utils/core"; import * as utils from "../../src/utils/getUri"; +import * as loggers from "../../src/utils/loggers"; import AuthSettings from "../../src/utils/secretStorage"; import { Telemetry } from "../../src/utils/telemetryClient"; @@ -1361,7 +1361,7 @@ describe("connectionManagerService", () => { connMngService, "retrieveConnectedConnection", ); - kdbOutputLogStub = sinon.stub(coreUtils, "kdbOutputLog"); + kdbOutputLogStub = sinon.stub(loggers, "kdbOutputLog"); showInformationMessageStub = sinon.stub(window, "showInformationMessage"); _showErrorMessageStub = sinon.stub(window, "showErrorMessage"); }); @@ -1375,7 +1375,7 @@ describe("connectionManagerService", () => { await connMngService.resetScratchpad(); sinon.assert.calledWith( kdbOutputLogStub, - "[RESET SCRATCHPAD] Please activate an Insights connection to use this feature.", + "[connectionManagerService] Please activate an Insights connection to use this feature.", "ERROR", ); }); @@ -1385,7 +1385,7 @@ describe("connectionManagerService", () => { await connMngService.resetScratchpad(); sinon.assert.calledWith( kdbOutputLogStub, - "[RESET SCRATCHPAD] Please activate an Insights connection to use this feature.", + "[connectionManagerService] Please activate an Insights connection to use this feature.", "ERROR", ); }); @@ -1413,8 +1413,8 @@ describe("connectionManagerService", () => { await connMngService.resetScratchpad("test"); sinon.assert.calledWith( kdbOutputLogStub, - "[RESET SCRATCHPAD] The user canceled the scratchpad reset.", - "INFO", + "[connectionManagerService] The user canceled the scratchpad reset.", + "DEBUG", ); }); @@ -1423,7 +1423,7 @@ describe("connectionManagerService", () => { await connMngService.resetScratchpad("test"); sinon.assert.calledWith( kdbOutputLogStub, - "[RESET SCRATCHPAD] Please connect to an Insights connection to use this feature.", + "[connectionManagerService] Please connect to an Insights connection to use this feature.", "ERROR", ); }); diff --git a/test/suite/utils.test.ts b/test/suite/utils.test.ts index ac432694..b7eeb030 100644 --- a/test/suite/utils.test.ts +++ b/test/suite/utils.test.ts @@ -55,6 +55,7 @@ import * as executionConsoleUtils from "../../src/utils/executionConsole"; import { feedbackSurveyDialog } from "../../src/utils/feedbackSurveyUtils"; import { getNonce } from "../../src/utils/getNonce"; import { getUri } from "../../src/utils/getUri"; +import * as loggers from "../../src/utils/loggers"; import { openUrl } from "../../src/utils/openUrl"; import * as queryUtils from "../../src/utils/queryUtils"; import { showRegistrationNotification } from "../../src/utils/registration"; @@ -92,7 +93,7 @@ describe("Utils", () => { let kdbOutputLogStub: sinon.SinonStub; beforeEach(() => { tryExecuteCommandStub = sinon.stub(cpUtils, "tryExecuteCommand"); - kdbOutputLogStub = sinon.stub(coreUtils, "kdbOutputLog"); + kdbOutputLogStub = sinon.stub(loggers, "kdbOutputLog"); }); afterEach(() => { @@ -319,7 +320,7 @@ describe("Utils", () => { const message = "test message"; const type = "INFO"; - coreUtils.kdbOutputLog(message, type); + loggers.kdbOutputLog(message, type); appendLineSpy.calledOnce; appendLineSpy.calledWithMatch(message); @@ -330,7 +331,7 @@ describe("Utils", () => { const message = "test message"; const type = "ERROR"; - coreUtils.kdbOutputLog(message, type); + loggers.kdbOutputLog(message, type); appendLineSpy.calledOnce; showErrorMessageSpy.calledOnce; @@ -1758,11 +1759,15 @@ describe("Utils", () => { get: sinon.stub(), update: sinon.stub(), }); - const logStub = sinon.stub(coreUtils, "kdbOutputLog"); + const logStub = sinon.stub(loggers, "kdbOutputLog"); LabelsUtils.createNewLabel("", "red"); - sinon.assert.calledWith(logStub, "Label name can't be empty", "ERROR"); + sinon.assert.calledWith( + logStub, + "[connLabel] Label name can't be empty.", + "ERROR", + ); }); it("should handle no color selected", () => { @@ -1770,13 +1775,13 @@ describe("Utils", () => { get: sinon.stub(), update: sinon.stub(), }); - const logStub = sinon.stub(coreUtils, "kdbOutputLog"); + const logStub = sinon.stub(loggers, "kdbOutputLog"); LabelsUtils.createNewLabel("label1", "randomColorName"); sinon.assert.calledWith( logStub, - "No Color selected for the label", + "[connLabel] No Color selected for the label.", "ERROR", ); }); @@ -2938,3 +2943,13 @@ describe("Utils", () => { }); }); }); + +describe("notifications", () => { + describe("Runner", () => { + it("should ", () => {}); + }); + + describe("showMessage", () => { + it("should ", () => {}); + }); +}); From 3b86698c11284c5f5a1871a70f0aab676ab6cd8b Mon Sep 17 00:00:00 2001 From: ecmel Date: Mon, 23 Jun 2025 14:31:00 +0300 Subject: [PATCH 02/10] added notebook run cancelllation --- src/classes/insightsConnection.ts | 5 +++++ src/services/notebookController.ts | 5 +++-- test/suite/utils.test.ts | 10 ---------- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/classes/insightsConnection.ts b/src/classes/insightsConnection.ts index 1880bc77..93380ae7 100644 --- a/src/classes/insightsConnection.ts +++ b/src/classes/insightsConnection.ts @@ -385,6 +385,11 @@ export class InsightsConnection { } options.responseType = "arraybuffer"; + showMessage("Requesting datasource query.", MessageKind.DEBUG, { + logger, + params: [options.url, options.data], + }); + const runner = Runner.create(async () => { return await axios(options) .then((response: any) => { diff --git a/src/services/notebookController.ts b/src/services/notebookController.ts index 908f7823..268e12d5 100644 --- a/src/services/notebookController.ts +++ b/src/services/notebookController.ts @@ -76,7 +76,7 @@ export class KxNotebookController { try { const results = await Promise.race([ - await manager.executeQuery( + manager.executeQuery( cell.document.getText(), conn.connLabel, ".", @@ -103,10 +103,11 @@ export class KxNotebookController { ]), ]); } catch (error) { - showMessage("Unable run code blocck.", MessageKind.ERROR, { + showMessage("Unable to run code block.", MessageKind.ERROR, { logger, params: [error], }); + break; } finally { execution.end(true, Date.now()); } diff --git a/test/suite/utils.test.ts b/test/suite/utils.test.ts index b7eeb030..0bdb50b8 100644 --- a/test/suite/utils.test.ts +++ b/test/suite/utils.test.ts @@ -2943,13 +2943,3 @@ describe("Utils", () => { }); }); }); - -describe("notifications", () => { - describe("Runner", () => { - it("should ", () => {}); - }); - - describe("showMessage", () => { - it("should ", () => {}); - }); -}); From bda9d09c4b168f73060520f41e400b6076cb7006 Mon Sep 17 00:00:00 2001 From: ecmel Date: Tue, 24 Jun 2025 10:25:47 +0300 Subject: [PATCH 03/10] added details button --- src/classes/insightsConnection.ts | 13 ++++--- src/classes/localConnection.ts | 6 +-- src/commands/dataSourceCommand.ts | 8 ++-- src/commands/serverCommand.ts | 2 +- src/extension.ts | 2 +- src/services/connectionManagerService.ts | 2 +- src/services/notebookController.ts | 3 +- src/utils/dataSource.ts | 2 +- src/utils/notifications.ts | 47 ++++++++++++++++++------ test/suite/commands.test.ts | 1 + 10 files changed, 57 insertions(+), 29 deletions(-) diff --git a/src/classes/insightsConnection.ts b/src/classes/insightsConnection.ts index 93380ae7..5eae0d26 100644 --- a/src/classes/insightsConnection.ts +++ b/src/classes/insightsConnection.ts @@ -387,7 +387,7 @@ export class InsightsConnection { showMessage("Requesting datasource query.", MessageKind.DEBUG, { logger, - params: [options.url, options.data], + params: { url: options.url, data: options.data }, }); const runner = Runner.create(async () => { @@ -412,7 +412,7 @@ export class InsightsConnection { showMessage( `Datasource run status: ${error.response.status}.`, MessageKind.DEBUG, - { logger, params: [error] }, + { logger, params: error }, ); return { error: { buffer: error.response.data }, @@ -474,7 +474,7 @@ export class InsightsConnection { showMessage( "Unable to create UDA request body.", MessageKind.ERROR, - { logger, params: [udaReqBody.error] }, + { logger, params: udaReqBody.error }, ); return; } @@ -511,13 +511,16 @@ export class InsightsConnection { if (response.data.error) { showMessage("Unable to populate scratchpad.", MessageKind.ERROR, { logger, - params: [response.data.errorMsg], + params: response.data.errorMsg, }); } else { showMessage( `Populated scratchpad, stored in ${variableName}.`, MessageKind.INFO, - { logger, params: [response.status, body.params] }, + { + logger, + params: { status: response.status, params: body.params }, + }, ); Telemetry.sendEvent( "Datasource." + dsTypeString + ".Scratchpad.Populated", diff --git a/src/classes/localConnection.ts b/src/classes/localConnection.ts index 76ae8927..c812d635 100644 --- a/src/classes/localConnection.ts +++ b/src/classes/localConnection.ts @@ -113,7 +113,7 @@ export class LocalConnection { showMessage( `Connection to server ${this.options.host}:${this.options.port} failed.`, MessageKind.ERROR, - { logger, params: [err] }, + { logger, params: err }, ); return; @@ -317,7 +317,7 @@ export class LocalConnection { showMessage( "Failed to retrieve kdb+ global variables.", MessageKind.ERROR, - { logger, params: [err] }, + { logger, params: err }, ); return; } @@ -363,7 +363,7 @@ export class LocalConnection { showMessage( "Failed to retrieve kdb+ reserved keywords.", MessageKind.ERROR, - { logger, params: [err] }, + { logger, params: err }, ); return; } diff --git a/src/commands/dataSourceCommand.ts b/src/commands/dataSourceCommand.ts index 8c14ee42..79813858 100644 --- a/src/commands/dataSourceCommand.ts +++ b/src/commands/dataSourceCommand.ts @@ -194,7 +194,7 @@ export async function runDataSource( if (!success) { showMessage("Datasource run failed.", MessageKind.ERROR, { logger, - params: [res.error], + params: res.error, }); } if (ext.isResultsTabVisible) { @@ -239,7 +239,7 @@ export async function runDataSource( } catch (error) { showMessage(`Datasource error: ${error}.`, MessageKind.ERROR, { logger, - params: [error], + params: error, }); DataSourcesPanel.running = false; } finally { @@ -458,7 +458,7 @@ export async function runUDADataSource( if (udaReqBody.error) { showMessage(`Datasource error.`, MessageKind.ERROR, { logger, - params: [udaReqBody.error], + params: udaReqBody.error, }); return udaReqBody; } @@ -508,7 +508,7 @@ export function parseError(error: GetDataError) { } else { showMessage(`Datasource error.`, MessageKind.ERROR, { logger, - params: [error], + params: error, }); return { error, diff --git a/src/commands/serverCommand.ts b/src/commands/serverCommand.ts index fe33e3e5..e80a2936 100644 --- a/src/commands/serverCommand.ts +++ b/src/commands/serverCommand.ts @@ -847,7 +847,7 @@ export async function _executeQuery( if (connLabel === "") { if (ext.activeConnection === undefined) { showMessage( - "No active connection found. Connect to one connection.", + "You aren't connected to any connection. Once connected please try again.", MessageKind.ERROR, ); return undefined; diff --git a/src/extension.ts b/src/extension.ts index 02fbde11..794f3366 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -888,7 +888,7 @@ function registerExecuteCommands(): CommandRegistration[] { runQFileTerminal(`"${uri.fsPath}"`); } catch (error) { showMessage(`Unable to write temp file.`, MessageKind.ERROR, { - params: [error], + params: error, }); } } diff --git a/src/services/connectionManagerService.ts b/src/services/connectionManagerService.ts index 60e2bc84..94aa6538 100644 --- a/src/services/connectionManagerService.ts +++ b/src/services/connectionManagerService.ts @@ -148,7 +148,7 @@ export class ConnectionManagementService { if (err) { showMessage(`Connection failed to: ${connLabel}`, MessageKind.ERROR, { logger, - params: [err], + params: err, }); this.isNotConnectedBehaviour(connLabel); return; diff --git a/src/services/notebookController.ts b/src/services/notebookController.ts index 268e12d5..5636f543 100644 --- a/src/services/notebookController.ts +++ b/src/services/notebookController.ts @@ -60,7 +60,6 @@ export class KxNotebookController { showMessage( "You aren't connected to any connection. Once connected please try again.", MessageKind.ERROR, - { logger }, ); return; } @@ -105,7 +104,7 @@ export class KxNotebookController { } catch (error) { showMessage("Unable to run code block.", MessageKind.ERROR, { logger, - params: [error], + params: error, }); break; } finally { diff --git a/src/utils/dataSource.ts b/src/utils/dataSource.ts index 2d20ca15..744d2c8f 100644 --- a/src/utils/dataSource.ts +++ b/src/utils/dataSource.ts @@ -58,7 +58,7 @@ export function convertTimeToTimestamp(time: string): string { showMessage( "The string param is in an incorrect format.", MessageKind.ERROR, - { logger, params: [time, error] }, + { logger, params: { time, error } }, ); return ""; } diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index 9fa84295..12d9bd6c 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -14,6 +14,7 @@ import * as vscode from "vscode"; import { kdbOutputLog } from "./loggers"; +import { ext } from "../extensionVariables"; const logger = "notifications"; @@ -98,24 +99,41 @@ export const enum MessageKind { export function showMessage( message: string, kind: MessageKind, - options: { logger?: string; params?: Array } = {}, + options: { logger?: string; params?: any } = {}, ...items: T[] ): Thenable { + message = stripUnprintableChars(message); if (options.logger) { const params = getParams(options.params); const log = `[${options.logger}] ${message} ${params}`.trim(); kdbOutputLog(log, kind, true); } - switch (kind) { - case MessageKind.ERROR: - return vscode.window.showErrorMessage(message, ...items); - case MessageKind.WARNING: - return vscode.window.showWarningMessage(message, ...items); - case MessageKind.INFO: - return vscode.window.showInformationMessage(message, ...items); - default: - return Promise.resolve(undefined); + + let action: "Details" | "OK" | undefined; + + if (items.length === 0) { + action = options.params ? "Details" : "OK"; + items.push(action); + } + + const dialog = + kind === MessageKind.ERROR + ? vscode.window.showErrorMessage(message, ...items) + : kind === MessageKind.WARNING + ? vscode.window.showWarningMessage(message, ...items) + : kind === MessageKind.INFO + ? vscode.window.showInformationMessage(message, ...items) + : Promise.resolve(undefined); + + if (action === "Details") { + dialog.then((res) => { + if (res === "Details") { + ext.outputChannel.show(true); + } + }); } + + return dialog; } function getParams(params?: Array) { @@ -123,9 +141,16 @@ function getParams(params?: Array) { try { return JSON.stringify(params); } catch (error) { - return JSON.stringify(error); + return `Parsing log params failed: ${error}`; } } else { return ""; } } + +function stripUnprintableChars(text: string) { + return text + .replace(/\p{Cc}/gu, "") + .replace(/\p{Co}/gu, "") + .replace(/\p{Cn}/gu, ""); +} diff --git a/test/suite/commands.test.ts b/test/suite/commands.test.ts index 51bf0b96..d1bc4a6f 100644 --- a/test/suite/commands.test.ts +++ b/test/suite/commands.test.ts @@ -2526,6 +2526,7 @@ describe("serverCommand", () => { sinon.assert.calledOnceWithExactly( showInfoStub, "Query copied to clipboard.", + "OK", ); }); From 10c13860d13551cdc29be0760ac59e636e92fcaa Mon Sep 17 00:00:00 2001 From: ecmel Date: Tue, 24 Jun 2025 15:00:16 +0300 Subject: [PATCH 04/10] unfifed telemetry --- src/classes/insightsConnection.ts | 46 ++++----- src/classes/localConnection.ts | 21 ++-- src/commands/dataSourceCommand.ts | 39 ++++--- src/commands/installTools.ts | 48 ++++----- src/commands/serverCommand.ts | 123 ++++++++++++----------- src/commands/walkthroughCommand.ts | 4 +- src/commands/workspaceCommand.ts | 20 ++-- src/extension.ts | 17 ++-- src/panels/newConnection.ts | 7 +- src/services/connectionManagerService.ts | 60 ++++++----- src/services/dataSourceEditorProvider.ts | 7 +- src/services/notebookController.ts | 7 +- src/services/resultsPanelProvider.ts | 10 +- src/utils/connLabel.ts | 6 +- src/utils/core.ts | 44 ++++---- src/utils/cpUtils.ts | 10 +- src/utils/dataSource.ts | 20 ++-- src/utils/execution.ts | 6 +- src/utils/executionConsole.ts | 11 +- src/utils/feedbackSurveyUtils.ts | 4 +- src/utils/notifications.ts | 26 ++++- src/utils/queryUtils.ts | 4 +- src/utils/registration.ts | 4 +- src/utils/shell.ts | 6 +- 24 files changed, 282 insertions(+), 268 deletions(-) diff --git a/src/classes/insightsConnection.ts b/src/classes/insightsConnection.ts index 5eae0d26..25d540b4 100644 --- a/src/classes/insightsConnection.ts +++ b/src/classes/insightsConnection.ts @@ -41,13 +41,12 @@ import { tokenUndefinedError, } from "../utils/core"; import { convertTimeToTimestamp } from "../utils/dataSource"; -import { MessageKind, Runner, showMessage } from "../utils/notifications"; +import { MessageKind, Runner, notify } from "../utils/notifications"; import { generateQSqlBody, handleScratchpadTableRes, handleWSResults, } from "../utils/queryUtils"; -import { Telemetry } from "../utils/telemetryClient"; import { retrieveUDAtoCreateReqBody } from "../utils/uda"; const logger = "insightsConnection"; @@ -154,7 +153,7 @@ export class InsightsConnection { public returnMetaObject(metaType: MetaInfoType): string { if (!this.meta) { - showMessage( + notify( `Meta data is undefined for connection ${this.connLabel}`, MessageKind.ERROR, { logger }, @@ -184,7 +183,7 @@ export class InsightsConnection { objectToReturn = this.meta.payload.rc; break; default: - showMessage(`Invalid meta type: ${metaType}`, MessageKind.ERROR, { + notify(`Invalid meta type: ${metaType}`, MessageKind.ERROR, { logger, }); return ""; @@ -385,7 +384,7 @@ export class InsightsConnection { } options.responseType = "arraybuffer"; - showMessage("Requesting datasource query.", MessageKind.DEBUG, { + notify("Requesting datasource query.", MessageKind.DEBUG, { logger, params: { url: options.url, data: options.data }, }); @@ -393,7 +392,7 @@ export class InsightsConnection { const runner = Runner.create(async () => { return await axios(options) .then((response: any) => { - showMessage( + notify( `Datasource run status: ${response.status}.`, MessageKind.DEBUG, { logger }, @@ -409,7 +408,7 @@ export class InsightsConnection { }; }) .catch((error: any) => { - showMessage( + notify( `Datasource run status: ${error.response.status}.`, MessageKind.DEBUG, { logger, params: error }, @@ -471,11 +470,10 @@ export class InsightsConnection { const udaReqBody = await retrieveUDAtoCreateReqBody(uda, this); if (udaReqBody.error) { - showMessage( - "Unable to create UDA request body.", - MessageKind.ERROR, - { logger, params: udaReqBody.error }, - ); + notify("Unable to create UDA request body.", MessageKind.ERROR, { + logger, + params: udaReqBody.error, + }); return; } body.params = udaReqBody.params; @@ -509,22 +507,21 @@ export class InsightsConnection { const runner = Runner.create(async () => { return await axios(options).then((response: any) => { if (response.data.error) { - showMessage("Unable to populate scratchpad.", MessageKind.ERROR, { + notify("Unable to populate scratchpad.", MessageKind.ERROR, { logger, params: response.data.errorMsg, }); } else { - showMessage( + notify( `Populated scratchpad, stored in ${variableName}.`, MessageKind.INFO, { logger, params: { status: response.status, params: body.params }, + telemetry: + "Datasource." + dsTypeString + ".Scratchpad.Populated", }, ); - Telemetry.sendEvent( - "Datasource." + dsTypeString + ".Scratchpad.Populated", - ); } }); }); @@ -592,7 +589,7 @@ export class InsightsConnection { if (response.data.error) { return response.data; } else { - showMessage(`Status: ${response.status}`, MessageKind.DEBUG, { + notify(`Status: ${response.status}`, MessageKind.DEBUG, { logger, }); if (!response.data.error) { @@ -671,13 +668,13 @@ export class InsightsConnection { if (response.data.error) { return response.data; } else if (query === "") { - showMessage( + notify( `Scratchpad created for connection: ${this.connLabel}.`, MessageKind.DEBUG, { logger }, ); } else { - showMessage(`Status: ${response.status}`, MessageKind.DEBUG, { + notify(`Status: ${response.status}`, MessageKind.DEBUG, { logger, }); if (!response.data.error) { @@ -738,16 +735,15 @@ export class InsightsConnection { progress.report({ message: "Reseting scratchpad..." }); const res = await axios(options) .then((_response: any) => { - showMessage( + notify( `Executed successfully, scratchpad reset at ${this.connLabel} connection.`, MessageKind.INFO, - { logger }, + { logger, telemetry: "Scratchpad.Reseted" }, ); - Telemetry.sendEvent("Scratchpad.Reseted"); return true; }) .catch((_error: any) => { - showMessage( + notify( `Error occurred while resetting scratchpad in connection ${this.connLabel}, try again.`, MessageKind.ERROR, { logger }, @@ -765,7 +761,7 @@ export class InsightsConnection { } public noConnectionOrEndpoints(): void { - showMessage( + notify( `No connection or endpoints defined for ${this.connLabel}`, MessageKind.ERROR, { logger }, diff --git a/src/classes/localConnection.ts b/src/classes/localConnection.ts index c812d635..9b169975 100644 --- a/src/classes/localConnection.ts +++ b/src/classes/localConnection.ts @@ -18,7 +18,7 @@ import { ext } from "../extensionVariables"; import { QueryResult, QueryResultType } from "../models/queryResult"; import { delay } from "../utils/core"; import { convertStringToArray, handleQueryResults } from "../utils/execution"; -import { MessageKind, showMessage } from "../utils/notifications"; +import { MessageKind, notify } from "../utils/notifications"; import { queryWrapper } from "../utils/queryUtils"; const logger = "localConnection"; @@ -87,7 +87,7 @@ export class LocalConnection { ext.serverProvider.reload(); if (this.connLabel.endsWith("[local]")) { - showMessage( + notify( `Connection to server ${this.options.host}:${this.options.port} failed.`, MessageKind.ERROR, {}, @@ -103,14 +103,14 @@ export class LocalConnection { } }); } else { - showMessage( + notify( `Connection to server ${this.options.host}:${this.options.port} failed.`, MessageKind.ERROR, { logger }, ); } - showMessage( + notify( `Connection to server ${this.options.host}:${this.options.port} failed.`, MessageKind.ERROR, { logger, params: err }, @@ -120,7 +120,7 @@ export class LocalConnection { } conn.addListener("close", () => { commands.executeCommand("kdb.connections.disconnect", this.connLabel); - showMessage( + notify( `Connection closed: ${this.options.host}:${this.options.port}`, MessageKind.DEBUG, { logger }, @@ -314,11 +314,10 @@ export class LocalConnection { '{[q] t:system"T";tm:@[{$[x>0;[system"T ",string x;1b];0b]};0;{0b}];r:$[tm;@[0;(q;::);{[tm; t; msgs] if[tm;system"T ",string t];\'msgs}[tm;t]];@[q;::;{\'x}]];if[tm;system"T ",string t];r}{do[1000;2+2];{@[{.z.ide.ns.r1:x;:.z.ide.ns.r1};x;{r:y;:r}[;x]]}({:x!{![sv[`;] each x cross `Tables`Functions`Variables; system each "afv" cross enlist[" "] cross enlist string x]} each x} [{raze x,.z.s\'[{x where{@[{1#get x};x;`]~1#.q}\'[x]}` sv\'x,\'key x]}`]),(enlist `.z)!flip (`.z.Tables`.z.Functions`.z.Variables)!(enlist 0#`;enlist `ac`bm`exit`pc`pd`pg`ph`pi`pm`po`pp`ps`pw`vs`ts`s`wc`wo`ws;enlist `a`b`e`f`h`i`k`K`l`o`q`u`w`W`x`X`n`N`p`P`z`Z`t`T`d`D`c`zd)}'; this.connection?.k(globalQuery, (err, result) => { if (err) { - showMessage( - "Failed to retrieve kdb+ global variables.", - MessageKind.ERROR, - { logger, params: err }, - ); + notify("Failed to retrieve kdb+ global variables.", MessageKind.ERROR, { + logger, + params: err, + }); return; } @@ -360,7 +359,7 @@ export class LocalConnection { const reservedQuery = ".Q.res"; this.connection?.k(reservedQuery, (err, result) => { if (err) { - showMessage( + notify( "Failed to retrieve kdb+ reserved keywords.", MessageKind.ERROR, { logger, params: err }, diff --git a/src/commands/dataSourceCommand.ts b/src/commands/dataSourceCommand.ts index 79813858..8f14be98 100644 --- a/src/commands/dataSourceCommand.ts +++ b/src/commands/dataSourceCommand.ts @@ -40,7 +40,7 @@ import { createKdbDataSourcesFolder, getConnectedInsightsNode, } from "../utils/dataSource"; -import { MessageKind, showMessage } from "../utils/notifications"; +import { MessageKind, notify } from "../utils/notifications"; import { addQueryHistory, generateQSqlBody, @@ -48,11 +48,10 @@ import { handleWSError, handleWSResults, } from "../utils/queryUtils"; -import { Telemetry } from "../utils/telemetryClient"; import { retrieveUDAtoCreateReqBody } from "../utils/uda"; import { validateScratchpadOutputVariableName } from "../validators/interfaceValidator"; -const logger = "dataSourceCommands"; +const logger = "dataSourceCommand"; export async function addDataSource(): Promise { const kdbDataSourcesFolderPath = createKdbDataSourcesFolder(); @@ -73,12 +72,11 @@ export async function addDataSource(): Promise { defaultDataSourceContent.insightsNode = insightsNode; fs.writeFileSync(filePath, JSON.stringify(defaultDataSourceContent)); - showMessage( + notify( `Created ${fileName} in ${kdbDataSourcesFolderPath}.`, MessageKind.INFO, - { logger }, + { logger, telemetry: "Datasource.Created" }, ); - Telemetry.sendEvent("Datasource.Created"); } export async function populateScratchpad( @@ -116,7 +114,7 @@ export async function populateScratchpad( qenvEnabled === "Enabled", ); } else { - showMessage( + notify( `Invalid scratchpad output variable name: ${outputVariable}`, MessageKind.ERROR, { logger }, @@ -157,15 +155,15 @@ export async function runDataSource( dataSourceForm.insightsNode = getConnectedInsightsNode(); const fileContent = dataSourceForm; - showMessage( - `Running ${fileContent.name} datasource...`, - MessageKind.DEBUG, - { logger }, - ); let res: any; const selectedType = getSelectedType(fileContent); ext.isDatasourceExecution = true; - Telemetry.sendEvent("Datasource." + selectedType + ".Run"); + + notify(`Running ${fileContent.name} datasource...`, MessageKind.DEBUG, { + logger, + telemetry: "Datasource." + selectedType + ".Run", + }); + switch (selectedType) { case "API": res = await runApiDataSource(fileContent, selectedConnection); @@ -192,7 +190,7 @@ export async function runDataSource( const query = getQuery(fileContent, selectedType); if (!success) { - showMessage("Datasource run failed.", MessageKind.ERROR, { + notify("Datasource run failed.", MessageKind.ERROR, { logger, params: res.error, }); @@ -200,7 +198,7 @@ export async function runDataSource( if (ext.isResultsTabVisible) { if (success) { const resultCount = typeof res === "string" ? "0" : res.rows.length; - showMessage(`Results: ${resultCount} rows`, MessageKind.DEBUG, { + notify(`Results: ${resultCount} rows`, MessageKind.DEBUG, { logger, }); } else if (!success) { @@ -216,7 +214,7 @@ export async function runDataSource( ); } else { if (success) { - showMessage( + notify( `Results is a string with length: ${res.length}`, MessageKind.DEBUG, { logger }, @@ -237,7 +235,7 @@ export async function runDataSource( addDStoQueryHistory(dataSourceForm, success, connLabel, executorName); } } catch (error) { - showMessage(`Datasource error: ${error}.`, MessageKind.ERROR, { + notify(`Datasource error: ${error}.`, MessageKind.ERROR, { logger, params: error, }); @@ -291,9 +289,10 @@ export async function runApiDataSource( fileContent.dataSource.api.endTS, ); if (!isTimeCorrect) { - showMessage( + notify( "The time parameters (startTS and endTS) are not correct, please check the format or if the startTS is before the endTS", MessageKind.ERROR, + { logger }, ); return; } @@ -456,7 +455,7 @@ export async function runUDADataSource( const udaReqBody = await retrieveUDAtoCreateReqBody(uda, selectedConn); if (udaReqBody.error) { - showMessage(`Datasource error.`, MessageKind.ERROR, { + notify(`Datasource error.`, MessageKind.ERROR, { logger, params: udaReqBody.error, }); @@ -506,7 +505,7 @@ export function parseError(error: GetDataError) { if (error instanceof Object && error.buffer) { return handleWSError(error.buffer); } else { - showMessage(`Datasource error.`, MessageKind.ERROR, { + notify(`Datasource error.`, MessageKind.ERROR, { logger, params: error, }); diff --git a/src/commands/installTools.ts b/src/commands/installTools.ts index d4a0da40..31a1c00f 100644 --- a/src/commands/installTools.ts +++ b/src/commands/installTools.ts @@ -60,9 +60,8 @@ import { updateServers, } from "../utils/core"; import { executeCommand } from "../utils/cpUtils"; -import { MessageKind, Runner, showMessage } from "../utils/notifications"; +import { MessageKind, Runner, notify } from "../utils/notifications"; import { openUrl } from "../utils/openUrl"; -import { Telemetry } from "../utils/telemetryClient"; import { validateServerPort } from "../validators/kdbValidator"; const logger = "install"; @@ -82,7 +81,7 @@ export async function installTools(): Promise { if (licenseTypeResult?.label === licenseAquire) { let licenseCancel; await openUrl(ext.kdbInstallUrl); - await showMessage( + await notify( licenseWorkflow.prompt, MessageKind.INFO, {}, @@ -133,7 +132,7 @@ export async function installTools(): Promise { const runner = Runner.create(async (progress, token) => { token.onCancellationRequested(() => { - showMessage("User cancelled the installation.", MessageKind.DEBUG, { + notify("User cancelled the installation.", MessageKind.DEBUG, { logger, }); }); @@ -144,13 +143,10 @@ export async function installTools(): Promise { progress.report({ increment: 20, message: "Getting the binaries..." }); const osFile = getOsFile(); if (osFile === undefined) { - showMessage( + notify( "Unsupported operating system, unable to download binaries.", MessageKind.ERROR, - { logger }, - ); - Telemetry.sendException( - new Error("Unsupported operating system, unable to download binaries"), + { logger, telemetry: true }, ); } else { const gpath = join(ext.context.globalStorageUri.fsPath, osFile); @@ -158,15 +154,11 @@ export async function installTools(): Promise { const runtimeUrl = `${ext.kdbDownloadPrefixUrl}${osFile}`; const response = await fetch(runtimeUrl); if (response.status > 200) { - Telemetry.sendException( - new Error("Invalid or unavailable download url."), - ); - - showMessage( - `Invalid or unavailable download url: ${runtimeUrl}`, - MessageKind.ERROR, - { logger }, - ); + notify("Invalid or unavailable download url.", MessageKind.ERROR, { + logger, + params: runtimeUrl, + telemetry: true, + }); exit(1); } await ensureDir(ext.context.globalStorageUri.fsPath); @@ -204,7 +196,7 @@ export async function installTools(): Promise { if (QHOME) { env.QHOME = QHOME; if (!pathExists(env.QHOME)) { - showMessage("QHOME path stored is empty", MessageKind.ERROR, { + notify("QHOME path stored is empty", MessageKind.ERROR, { logger, }); } @@ -212,14 +204,14 @@ export async function installTools(): Promise { join(__dirname, "qinstall.md"), `# q runtime installed location: \n### ${QHOME}`, ); - showMessage(`Installation of q found here: ${QHOME}`, MessageKind.DEBUG, { + notify(`Installation of q found here: ${QHOME}`, MessageKind.DEBUG, { logger, }); } }); runner.title = "Installing q..."; runner.execute().then(async () => { - showMessage( + notify( onboardingWorkflow.prompt(ext.context.globalStorageUri.fsPath), MessageKind.INFO, {}, @@ -238,12 +230,10 @@ export async function installTools(): Promise { if (port) { let servers: Server | undefined = getServers(); if (servers != undefined && servers[getKeyForServerName("local")]) { - Telemetry.sendEvent( - `Server localhost:${port} already exists in configuration store.`, - ); - showMessage( - `Server localhost:${port} already exists.`, + notify( + `Server localhost:${port} already exists in configuration store`, MessageKind.ERROR, + { logger, telemetry: true }, ); } else { const key = "local"; @@ -309,7 +299,7 @@ export async function startLocalProcessByServerName( ); } catch { await removeLocalConnectionStatus(serverName); - showMessage("Error starting q process.", MessageKind.ERROR); + notify("Error starting q process.", MessageKind.ERROR, { logger }); } } @@ -323,7 +313,7 @@ export async function startLocalProcess(viewItem: KdbNode): Promise { export async function stopLocalProcess(viewItem: KdbNode): Promise { ext.localProcessObjects[viewItem.children[0]].kill(); - showMessage( + notify( `Child process id ${ext.localProcessObjects[viewItem.children[0]] .pid!} removed in cache.`, MessageKind.DEBUG, @@ -336,7 +326,7 @@ export async function stopLocalProcessByServerName( serverName: string, ): Promise { ext.localProcessObjects[serverName].kill(); - showMessage( + notify( `Child process id ${ext.localProcessObjects[serverName].pid!} removed in cache.`, MessageKind.DEBUG, { logger }, diff --git a/src/commands/serverCommand.ts b/src/commands/serverCommand.ts index e80a2936..28cc95f8 100644 --- a/src/commands/serverCommand.ts +++ b/src/commands/serverCommand.ts @@ -71,7 +71,7 @@ import { import { refreshDataSourcesPanel } from "../utils/dataSource"; import { decodeQUTF } from "../utils/decode"; import { ExecutionConsole } from "../utils/executionConsole"; -import { MessageKind, Runner, showMessage } from "../utils/notifications"; +import { MessageKind, Runner, notify } from "../utils/notifications"; import { openUrl } from "../utils/openUrl"; import { checkIfIsDatasource, @@ -79,7 +79,6 @@ import { formatScratchpadStacktrace, resultToBase64, } from "../utils/queryUtils"; -import { Telemetry } from "../utils/telemetryClient"; import { addWorkspaceFile, openWith, @@ -116,7 +115,7 @@ export async function addInsightsConnection( ) { const aliasValidation = validateServerAlias(insightsData.alias, false); if (aliasValidation) { - showMessage(aliasValidation, MessageKind.ERROR); + notify(aliasValidation, MessageKind.ERROR, { logger }); return; } if (insightsData.alias === undefined || insightsData.alias === "") { @@ -129,9 +128,10 @@ export async function addInsightsConnection( insights != undefined && insights[getKeyForServerName(insightsData.alias)] ) { - showMessage( + notify( `Insights instance named ${insightsData.alias} already exists.`, MessageKind.ERROR, + { logger }, ); return; } else { @@ -169,10 +169,12 @@ export async function addInsightsConnection( await handleLabelsConnMap(labels, insightsData.alias); } ext.serverProvider.refreshInsights(newInsights); - Telemetry.sendEvent("Connection.Created.Insights"); + notify("Created Insights connection.", MessageKind.DEBUG, { + telemetry: "Connection.Created.Insights", + }); } - showMessage( + notify( `Added Insights connection: ${insightsData.alias}`, MessageKind.INFO, ); @@ -192,7 +194,7 @@ export async function editInsightsConnection( ? undefined : validateServerAlias(insightsData.alias, false); if (aliasValidation) { - showMessage(aliasValidation, MessageKind.ERROR); + notify(aliasValidation, MessageKind.ERROR, { logger }); return; } const isConnectedConn = isConnected(oldAlias); @@ -209,16 +211,18 @@ export async function editInsightsConnection( ? insights[getKeyForServerName(insightsData.alias)] : undefined; if (newAliasExists) { - showMessage( + notify( `Insights instance named ${insightsData.alias} already exists.`, MessageKind.ERROR, + { logger }, ); return; } else { if (!oldInsights) { - showMessage( + notify( `Insights instance named ${oldAlias} does not exist.`, MessageKind.ERROR, + { logger }, ); return; } else { @@ -264,13 +268,15 @@ export async function editInsightsConnection( removeConnFromLabels(insightsData.alias); } ext.serverProvider.refreshInsights(newInsights); - Telemetry.sendEvent("Connection.Edited.Insights"); + notify("Edited Insights connection.", MessageKind.DEBUG, { + telemetry: "Connection.Edited.Insights", + }); if (isConnectedConn) { offerReconnectionAfterEdit(insightsData.alias); } } - showMessage( + notify( `Edited Insights connection: ${insightsData.alias}`, MessageKind.INFO, ); @@ -290,7 +296,7 @@ export async function addAuthConnection( ): Promise { const validUsername = validateServerUsername(username); if (validUsername) { - showMessage(validUsername, MessageKind.ERROR); + notify(validUsername, MessageKind.ERROR, { logger }); return; } if (password?.trim()?.length) { @@ -332,10 +338,10 @@ export async function enableTLS(serverKey: string): Promise { // validate if TLS is possible if (ext.openSslVersion === null) { - showMessage( + notify( "OpenSSL not found, please ensure this is installed", MessageKind.ERROR, - {}, + { logger }, "More Info", "Cancel", ).then(async (result) => { @@ -354,10 +360,10 @@ export async function enableTLS(serverKey: string): Promise { } return; } - showMessage( + notify( "Server not found, please ensure this is a correct server", MessageKind.ERROR, - {}, + { logger }, "Cancel", ); } @@ -371,15 +377,15 @@ export async function addKdbConnection( const hostnameValidation = validateServerName(kdbData.serverName); const portValidation = validateServerPort(kdbData.serverPort); if (aliasValidation) { - showMessage(aliasValidation, MessageKind.ERROR); + notify(aliasValidation, MessageKind.ERROR, { logger }); return; } if (hostnameValidation) { - showMessage(hostnameValidation, MessageKind.ERROR); + notify(hostnameValidation, MessageKind.ERROR, { logger }); return; } if (portValidation) { - showMessage(portValidation, MessageKind.ERROR); + notify(portValidation, MessageKind.ERROR, { logger }); return; } let servers: Server | undefined = getServers(); @@ -388,9 +394,10 @@ export async function addKdbConnection( servers != undefined && servers[getKeyForServerName(kdbData.serverAlias || "")] ) { - showMessage( + notify( `Server name ${kdbData.serverAlias} already exists.`, MessageKind.ERROR, + { logger }, ); } else { const key = kdbData.serverAlias || ""; @@ -430,17 +437,16 @@ export async function addKdbConnection( ext.latestLblsChanged.push(...labels); await handleLabelsConnMap(labels, kdbData.serverAlias); } - Telemetry.sendEvent("Connection.Created.QProcess"); + notify("Created kdb connection.", MessageKind.DEBUG, { + telemetry: "Connection.Created.QProcess", + }); ext.serverProvider.refresh(newServers); } if (kdbData.auth) { addAuthConnection(key, kdbData.username!, kdbData.password!); } - showMessage( - `Added kdb connection: ${kdbData.serverAlias}`, - MessageKind.INFO, - ); + notify(`Added kdb connection: ${kdbData.serverAlias}`, MessageKind.INFO); NewConnectionPannel.close(); } @@ -461,15 +467,15 @@ export async function editKdbConnection( const hostnameValidation = validateServerName(kdbData.serverName); const portValidation = validateServerPort(kdbData.serverPort); if (aliasValidation) { - showMessage(aliasValidation, MessageKind.ERROR); + notify(aliasValidation, MessageKind.ERROR, { logger }); return; } if (hostnameValidation) { - showMessage(hostnameValidation, MessageKind.ERROR); + notify(hostnameValidation, MessageKind.ERROR, { logger }); return; } if (portValidation) { - showMessage(portValidation, MessageKind.ERROR); + notify(portValidation, MessageKind.ERROR, { logger }); return; } const isConnectedConn = isConnected(oldAlias); @@ -483,16 +489,18 @@ export async function editKdbConnection( ? servers[getKeyForServerName(kdbData.serverAlias)] : undefined; if (newAliasExists) { - showMessage( + notify( `KDB instance named ${kdbData.serverAlias} already exists.`, MessageKind.ERROR, + { logger }, ); return; } else { if (!oldServer) { - showMessage( + notify( `KDB instance named ${oldAlias} does not exist.`, MessageKind.ERROR, + { logger }, ); return; } else { @@ -542,14 +550,16 @@ export async function editKdbConnection( removeConnFromLabels(kdbData.serverAlias); } ext.serverProvider.refresh(newServers); - Telemetry.sendEvent("Connection.Edited.KDB"); + notify("Edited kdb connection.", MessageKind.DEBUG, { + telemetry: "Connection.Edited.KDB", + }); const connLabelToReconn = `${kdbData.serverName}:${kdbData.serverPort} [${kdbData.serverAlias}]`; if (isConnectedConn) { offerReconnectionAfterEdit(connLabelToReconn); } } - showMessage( + notify( `Edited KDB connection: ${kdbData.serverAlias}`, MessageKind.INFO, ); @@ -588,7 +598,7 @@ export async function importConnections() { const fileUri = await window.showOpenDialog(options); if (!fileUri || fileUri.length === 0) { - showMessage("No file selected.", MessageKind.ERROR, { logger }); + notify("No file selected.", MessageKind.ERROR, { logger }); return; } const filePath = fileUri[0].fsPath; @@ -598,12 +608,12 @@ export async function importConnections() { try { importedConnections = JSON.parse(fileContent); } catch { - showMessage("Invalid JSON format.", MessageKind.ERROR, { logger }); + notify("Invalid JSON format.", MessageKind.ERROR, { logger }); return; } if (!isValidExportedConnections(importedConnections)) { - showMessage("JSON does not match the required format.", MessageKind.ERROR, { + notify("JSON does not match the required format.", MessageKind.ERROR, { logger, }); return; @@ -612,7 +622,7 @@ export async function importConnections() { importedConnections.connections.KDB.length === 0 && importedConnections.connections.Insights.length === 0 ) { - showMessage( + notify( "There is no KDB or Insights connections to import in this JSON file.", MessageKind.ERROR, { logger }, @@ -647,7 +657,7 @@ export async function addImportedConnections( let res: "Duplicate" | "Overwrite" | "Cancel" | undefined = "Duplicate"; if (hasDuplicates) { - res = await showMessage( + res = await notify( "You are importing connections with the same name. Would you like to duplicate, overwrite or cancel the import?", MessageKind.INFO, {}, @@ -720,7 +730,7 @@ export async function addImportedConnections( ext.serverProvider.refresh(config); } - showMessage("Connections imported successfully.", MessageKind.INFO, { + notify("Connections imported successfully.", MessageKind.INFO, { logger, }); } @@ -740,7 +750,7 @@ export async function connect(connLabel: string): Promise { ExecutionConsole.start(); const viewItem = connMngService.retrieveConnection(connLabel); if (viewItem === undefined) { - showMessage("Connection not found.", MessageKind.ERROR); + notify("Connection not found.", MessageKind.ERROR, { logger }); return; } @@ -749,7 +759,7 @@ export async function connect(connLabel: string): Promise { // check for TLS support if (viewItem.details.tls) { if (!(await checkOpenSslInstalled())) { - showMessage( + notify( "TLS support requires OpenSSL to be installed.", MessageKind.INFO, {}, @@ -846,9 +856,10 @@ export async function _executeQuery( const queryConsole = ExecutionConsole.start(); if (connLabel === "") { if (ext.activeConnection === undefined) { - showMessage( + notify( "You aren't connected to any connection. Once connected please try again.", MessageKind.ERROR, + { logger }, ); return undefined; } else { @@ -857,7 +868,9 @@ export async function _executeQuery( } const isConnected = connMngService.isConnected(connLabel); if (!isConnected) { - showMessage("The selected connection is not connected.", MessageKind.ERROR); + notify("The selected connection is not connected.", MessageKind.ERROR, { + logger, + }); return undefined; } @@ -1104,7 +1117,7 @@ export function copyQuery(queryHistoryElement: QueryHistory) { typeof queryHistoryElement.query === "string" ) { env.clipboard.writeText(queryHistoryElement.query); - showMessage("Query copied to clipboard.", MessageKind.INFO); + notify("Query copied to clipboard.", MessageKind.INFO); } } @@ -1115,7 +1128,7 @@ export async function loadServerObjects(): Promise { ext.activeConnection.connected === false || ext.activeConnection instanceof InsightsConnection ) { - showMessage( + notify( "Please connect to a KDB instance to view the objects", MessageKind.INFO, ); @@ -1153,7 +1166,7 @@ export async function openMeta(node: MetaObjectPayloadNode | InsightsMetaNode) { viewColumn: ViewColumn.One, }); } else { - showMessage("Meta content not found.", MessageKind.ERROR, { + notify("Meta content not found.", MessageKind.ERROR, { logger, }); } @@ -1167,11 +1180,9 @@ export async function exportConnections(connLabel?: string) { }); if (!exportAuth) { - showMessage( - "Export operation was cancelled by the user.", - MessageKind.DEBUG, - { logger }, - ); + notify("Export operation was cancelled by the user.", MessageKind.DEBUG, { + logger, + }); return; } @@ -1192,7 +1203,7 @@ export async function exportConnections(connLabel?: string) { if (uri) { fs.writeFile(uri.fsPath, formattedDoc, (err) => { if (err) { - showMessage(`Error saving file: ${err.message}`, MessageKind.ERROR, { + notify(`Error saving file: ${err.message}`, MessageKind.ERROR, { logger, }); } else { @@ -1202,14 +1213,12 @@ export async function exportConnections(connLabel?: string) { } }); } else { - showMessage( - "Save operation was cancelled by the user.", - MessageKind.DEBUG, - { logger }, - ); + notify("Save operation was cancelled by the user.", MessageKind.DEBUG, { + logger, + }); } } else { - showMessage("No connections found to be exported.", MessageKind.ERROR, { + notify("No connections found to be exported.", MessageKind.ERROR, { logger, }); } diff --git a/src/commands/walkthroughCommand.ts b/src/commands/walkthroughCommand.ts index c649a1a9..646dec03 100644 --- a/src/commands/walkthroughCommand.ts +++ b/src/commands/walkthroughCommand.ts @@ -13,11 +13,11 @@ import { workspace } from "vscode"; -import { MessageKind, showMessage } from "../utils/notifications"; +import { MessageKind, notify } from "../utils/notifications"; export async function showInstallationDetails(): Promise { const QHOME = await workspace .getConfiguration() .get("kdb.qHomeDirectory"); - showMessage(`q runtime installed path: ${QHOME}`, MessageKind.INFO); + notify(`q runtime installed path: ${QHOME}`, MessageKind.INFO); } diff --git a/src/commands/workspaceCommand.ts b/src/commands/workspaceCommand.ts index f9821ef1..5b13abea 100644 --- a/src/commands/workspaceCommand.ts +++ b/src/commands/workspaceCommand.ts @@ -35,7 +35,7 @@ import { ConnectionManagementService } from "../services/connectionManagerServic import { InsightsNode, KdbNode, LabelNode } from "../services/kdbTreeProvider"; import { offerConnectAction } from "../utils/core"; import { importOldDsFiles, oldFilesExists } from "../utils/dataSource"; -import { MessageKind, showMessage } from "../utils/notifications"; +import { MessageKind, notify } from "../utils/notifications"; import { normalizeAssemblyTarget } from "../utils/shared"; const logger = "workspaceCommand"; @@ -407,7 +407,7 @@ export async function importOldDSFiles() { if (ext.oldDSformatExists) { const folders = workspace.workspaceFolders; if (!folders) { - showMessage("No workspace folder found.", MessageKind.ERROR); + notify("No workspace folder found.", MessageKind.ERROR, { logger }); return; } return await window.withProgress( @@ -417,11 +417,9 @@ export async function importOldDSFiles() { }, async (progress, token) => { token.onCancellationRequested(() => { - showMessage( - "User cancelled the old DS files import.", - MessageKind.DEBUG, - { logger }, - ); + notify("User cancelled the old DS files import.", MessageKind.DEBUG, { + logger, + }); return false; }); @@ -431,10 +429,8 @@ export async function importOldDSFiles() { }, ); } else { - showMessage( - "No old Datasource files found on your VSCODE.", - MessageKind.INFO, - { logger }, - ); + notify("No old Datasource files found on your VSCODE.", MessageKind.INFO, { + logger, + }); } } diff --git a/src/extension.ts b/src/extension.ts index 794f3366..1407e7c8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -116,7 +116,7 @@ import { } from "./utils/core"; import { runQFileTerminal } from "./utils/execution"; import { handleFeedbackSurvey } from "./utils/feedbackSurveyUtils"; -import { MessageKind, showMessage } from "./utils/notifications"; +import { MessageKind, notify } from "./utils/notifications"; import AuthSettings from "./utils/secretStorage"; import { Telemetry } from "./utils/telemetryClient"; import { @@ -202,7 +202,7 @@ export async function activate(context: vscode.ExtensionContext) { // check for installed q runtime await checkLocalInstall(true); } catch (err) { - showMessage(`${err}`, MessageKind.DEBUG, { logger }); + notify(`${err}`, MessageKind.DEBUG, { logger }); } registerAllExtensionCommands(); @@ -312,7 +312,6 @@ export async function activate(context: vscode.ExtensionContext) { connectClientCommands(context, client); - Telemetry.sendEvent("Extension.Activated"); const yamlExtension = vscode.extensions.getExtension("redhat.vscode-yaml"); if (yamlExtension) { const actualSchema = await vscode.workspace @@ -350,7 +349,10 @@ export async function activate(context: vscode.ExtensionContext) { } handleFeedbackSurvey(); - showMessage("kdb extension is now active.", MessageKind.DEBUG, { logger }); + notify("kdb extension is now active.", MessageKind.DEBUG, { + logger, + telemetry: "Extension.Activated", + }); } function registerHelpCommands(): CommandRegistration[] { @@ -670,7 +672,9 @@ function registerConnectionsCommands(): CommandRegistration[] { true, ); } else { - showMessage("Connection label not found", MessageKind.ERROR); + notify("Connection label not found", MessageKind.ERROR, { + logger, + }); } }, }, @@ -887,7 +891,8 @@ function registerExecuteCommands(): CommandRegistration[] { ); runQFileTerminal(`"${uri.fsPath}"`); } catch (error) { - showMessage(`Unable to write temp file.`, MessageKind.ERROR, { + notify(`Unable to write temp file.`, MessageKind.ERROR, { + logger, params: error, }); } diff --git a/src/panels/newConnection.ts b/src/panels/newConnection.ts index 56ea3a21..3ac9aa69 100644 --- a/src/panels/newConnection.ts +++ b/src/panels/newConnection.ts @@ -20,7 +20,9 @@ import { InsightsNode, KdbNode } from "../services/kdbTreeProvider"; import { retrieveConnLabelsNames } from "../utils/connLabel"; import { getNonce } from "../utils/getNonce"; import { getUri } from "../utils/getUri"; -import { MessageKind, showMessage } from "../utils/notifications"; +import { MessageKind, notify } from "../utils/notifications"; + +const logger = "newConnection"; export class NewConnectionPannel { public static currentPanel: NewConnectionPannel | undefined; @@ -97,9 +99,10 @@ export class NewConnectionPannel { this._panel.webview.onDidReceiveMessage((message) => { if (message.command === "kdb.connections.add.bundleq") { if (ext.isBundleQCreated) { - showMessage( + notify( "Bundled Q is already created, please remove it first", MessageKind.ERROR, + { logger }, ); } else { vscode.commands.executeCommand( diff --git a/src/services/connectionManagerService.ts b/src/services/connectionManagerService.ts index 94aa6538..646214ea 100644 --- a/src/services/connectionManagerService.ts +++ b/src/services/connectionManagerService.ts @@ -36,9 +36,8 @@ import { updateServers, } from "../utils/core"; import { refreshDataSourcesPanel } from "../utils/dataSource"; -import { MessageKind, showMessage } from "../utils/notifications"; +import { MessageKind, notify } from "../utils/notifications"; import { sanitizeQuery } from "../utils/queryUtils"; -import { Telemetry } from "../utils/telemetryClient"; const logger = "connectionManagerService"; @@ -146,7 +145,7 @@ export class ConnectionManagementService { ); await localConnection.connect((err, conn) => { if (err) { - showMessage(`Connection failed to: ${connLabel}`, MessageKind.ERROR, { + notify(`Connection failed to: ${connLabel}`, MessageKind.ERROR, { logger, params: err, }); @@ -154,14 +153,12 @@ export class ConnectionManagementService { return; } if (conn) { - showMessage( + notify( `Connection established successfully to: ${connLabel}`, MessageKind.DEBUG, - { logger }, + { logger, telemetry: "Connection.Connected.QProcess" }, ); - Telemetry.sendEvent("Connection.Connected.QProcess"); - ext.connectedConnectionList.push(localConnection); this.isConnectedBehaviour(connection); @@ -175,13 +172,12 @@ export class ConnectionManagementService { ); await insightsConn.connect(); if (insightsConn.connected) { - Telemetry.sendEvent("Connection.Connected.Insights"); - showMessage( + notify( `Connection established successfully to: ${connLabel}`, MessageKind.DEBUG, - { logger }, + { logger, telemetry: "Connection.Connected.Insights" }, ); - showMessage( + notify( `${connLabel} connection insights version: ${insightsConn.insightsVersion}`, MessageKind.DEBUG, { logger }, @@ -203,7 +199,9 @@ export class ConnectionManagementService { commands.executeCommand("setContext", "kdb.connected.active", [ `${node.label}`, ]); - Telemetry.sendEvent("Connection.Connected.Active"); + notify("Connection activated.", MessageKind.DEBUG, { + telemetry: "Connection.Connected.Active", + }); ext.activeConnection = connection; if (node instanceof InsightsNode) { @@ -296,8 +294,10 @@ export class ConnectionManagementService { } public isNotConnectedBehaviour(connLabel: string): void { - showMessage(`Connection failed to: ${connLabel}`, MessageKind.ERROR); - Telemetry.sendEvent("Connection.Failed"); + notify(`Connection failed to: ${connLabel}`, MessageKind.ERROR, { + logger, + telemetry: "Connection.Failed", + }); } public disconnectBehaviour( @@ -323,12 +323,10 @@ export class ConnectionManagementService { commands.executeCommand("setContext", "kdb.connected.active", false); commands.executeCommand("setContext", "kdb.pythonEnabled", false); } - Telemetry.sendEvent("Connection.Disconnected." + connType); - showMessage( - `Connection closed: ${connection.connLabel}`, - MessageKind.DEBUG, - { logger }, - ); + notify(`Connection closed: ${connection.connLabel}`, MessageKind.DEBUG, { + logger, + telemetry: "Connection.Disconnected." + connType, + }); ext.serverProvider.reload(); } @@ -377,7 +375,7 @@ export class ConnectionManagementService { if (retrievedConn instanceof InsightsConnection) { conn = retrievedConn; } else { - showMessage( + notify( "Please connect to an Insights connection to use this feature.", MessageKind.ERROR, { logger }, @@ -390,7 +388,7 @@ export class ConnectionManagementService { !ext.activeConnection || !(ext.activeConnection instanceof InsightsConnection) ) { - showMessage( + notify( "Please activate an Insights connection to use this feature.", MessageKind.ERROR, { logger }, @@ -405,7 +403,7 @@ export class ConnectionManagementService { isBaseVersionGreaterOrEqual(conn.insightsVersion, 1.13) ) { const confirmationPrompt = `Reset Scratchpad? All data in the ${conn.connLabel} Scratchpad will be lost, and variables will be reset.`; - const selection = await showMessage( + const selection = await notify( confirmationPrompt, MessageKind.INFO, {}, @@ -416,15 +414,13 @@ export class ConnectionManagementService { if (selection === "Yes") { await conn.resetScratchpad(); } else { - showMessage( - "The user canceled the scratchpad reset.", - MessageKind.DEBUG, - { logger }, - ); + notify("The user canceled the scratchpad reset.", MessageKind.DEBUG, { + logger, + }); return; } } else { - showMessage( + notify( "Please connect to an Insights connection with version 1.13 or higher.", MessageKind.ERROR, { logger }, @@ -460,7 +456,7 @@ export class ConnectionManagementService { ): string { const metaType = this.getMetaInfoType(metaTypeString.toUpperCase()); if (!metaType) { - showMessage( + notify( "The meta info type that you try to open is not valid", MessageKind.ERROR, { logger }, @@ -469,7 +465,7 @@ export class ConnectionManagementService { } const connection = this.retrieveConnectedConnection(connLabel); if (!connection) { - showMessage( + notify( "The connection that you try to open meta info is not connected", MessageKind.ERROR, { logger }, @@ -477,7 +473,7 @@ export class ConnectionManagementService { return ""; } if (connection instanceof LocalConnection) { - showMessage( + notify( "The connection that you try to open meta info is not an Insights connection", MessageKind.ERROR, { logger }, diff --git a/src/services/dataSourceEditorProvider.ts b/src/services/dataSourceEditorProvider.ts index 63512850..eba4b900 100644 --- a/src/services/dataSourceEditorProvider.ts +++ b/src/services/dataSourceEditorProvider.ts @@ -45,9 +45,11 @@ import { UDA } from "../models/uda"; import { offerConnectAction } from "../utils/core"; import { getNonce } from "../utils/getNonce"; import { getUri } from "../utils/getUri"; -import { MessageKind, Runner, showMessage } from "../utils/notifications"; +import { MessageKind, Runner, notify } from "../utils/notifications"; import { parseUDAList } from "../utils/uda"; +const logger = "dataSourceEditorProvider"; + export class DataSourceEditorProvider implements CustomTextEditorProvider { public filenname = ""; static readonly viewType = "kdb.dataSourceEditor"; @@ -92,9 +94,10 @@ export class DataSourceEditorProvider implements CustomTextEditorProvider { this.cache.set(connLabel, meta); } catch { - showMessage( + notify( "No database running in this Insights connection.", MessageKind.ERROR, + { logger }, ); meta = Promise.resolve({}); this.cache.set(connLabel, meta); diff --git a/src/services/notebookController.ts b/src/services/notebookController.ts index 5636f543..68abc49f 100644 --- a/src/services/notebookController.ts +++ b/src/services/notebookController.ts @@ -16,7 +16,7 @@ import * as vscode from "vscode"; import { InsightsConnection } from "../classes/insightsConnection"; import { ext } from "../extensionVariables"; import { ConnectionManagementService } from "../services/connectionManagerService"; -import { MessageKind, showMessage, timeout } from "../utils/notifications"; +import { MessageKind, notify, timeout } from "../utils/notifications"; import { resultToBase64 } from "../utils/queryUtils"; import { convertToGrid, formatResult } from "../utils/resultsRenderer"; @@ -57,9 +57,10 @@ export class KxNotebookController { ): Promise { const conn = ext.activeConnection; if (conn === undefined) { - showMessage( + notify( "You aren't connected to any connection. Once connected please try again.", MessageKind.ERROR, + { logger }, ); return; } @@ -102,7 +103,7 @@ export class KxNotebookController { ]), ]); } catch (error) { - showMessage("Unable to run code block.", MessageKind.ERROR, { + notify("Unable to run code block.", MessageKind.ERROR, { logger, params: error, }); diff --git a/src/services/resultsPanelProvider.ts b/src/services/resultsPanelProvider.ts index e717697b..ec7e8ff2 100644 --- a/src/services/resultsPanelProvider.ts +++ b/src/services/resultsPanelProvider.ts @@ -25,7 +25,7 @@ import { ext } from "../extensionVariables"; import * as utils from "../utils/execution"; import { getNonce } from "../utils/getNonce"; import { getUri } from "../utils/getUri"; -import { MessageKind, showMessage } from "../utils/notifications"; +import { MessageKind, notify } from "../utils/notifications"; import { convertToGrid, formatResult } from "../utils/resultsRenderer"; const logger = "resultsPanelProvider"; @@ -103,12 +103,14 @@ export class KdbResultsViewProvider implements WebviewViewProvider { exportToCsv() { if (ext.resultPanelCSV === "") { - showMessage("No results to export", MessageKind.ERROR); + notify("No results to export", MessageKind.ERROR, { logger }); return; } const workspaceFolders = workspace.workspaceFolders; if (!workspaceFolders) { - showMessage("Open a folder to export results", MessageKind.ERROR); + notify("Open a folder to export results", MessageKind.ERROR, { + logger, + }); return; } const workspaceUri = workspaceFolders[0].uri; @@ -139,7 +141,7 @@ export class KdbResultsViewProvider implements WebviewViewProvider { let gridOptions = undefined; if (!this._view) { - showMessage("No view to update", MessageKind.ERROR, { + notify("No view to update", MessageKind.ERROR, { logger, }); return; diff --git a/src/utils/connLabel.ts b/src/utils/connLabel.ts index efa48edd..c6ec141f 100644 --- a/src/utils/connLabel.ts +++ b/src/utils/connLabel.ts @@ -14,7 +14,7 @@ import { workspace } from "vscode"; import { ext } from "../extensionVariables"; -import { MessageKind, showMessage } from "./notifications"; +import { MessageKind, notify } from "./notifications"; import { ConnectionLabel, Labels } from "../models/labels"; import { NewConnectionPannel } from "../panels/newConnection"; import { InsightsNode, KdbNode } from "../services/kdbTreeProvider"; @@ -39,7 +39,7 @@ export function createNewLabel(name: string, colorName: string) { (color) => color.name.toLowerCase() === colorName.toLowerCase(), ); if (name === "") { - showMessage("Label name can't be empty.", MessageKind.ERROR, { logger }); + notify("Label name can't be empty.", MessageKind.ERROR, { logger }); } if (color && name !== "") { const newLbl: Labels = { @@ -51,7 +51,7 @@ export function createNewLabel(name: string, colorName: string) { .getConfiguration() .update("kdb.connectionLabels", ext.connLabelList, true); } else { - showMessage("No Color selected for the label.", MessageKind.ERROR, { + notify("No Color selected for the label.", MessageKind.ERROR, { logger, }); } diff --git a/src/utils/core.ts b/src/utils/core.ts index c0276e25..830e32b4 100644 --- a/src/utils/core.ts +++ b/src/utils/core.ts @@ -24,9 +24,8 @@ import { commands, ConfigurationTarget, Uri, workspace } from "vscode"; import { installTools } from "../commands/installTools"; import { ext } from "../extensionVariables"; import { tryExecuteCommand } from "./cpUtils"; -import { MessageKind, showMessage } from "./notifications"; +import { MessageKind, notify } from "./notifications"; import { showRegistrationNotification } from "./registration"; -import { Telemetry } from "./telemetryClient"; import { InsightDetails, Insights, @@ -38,7 +37,7 @@ import { QueryResult } from "../models/queryResult"; const logger = "core"; export function log(childProcess: ChildProcess): void { - showMessage(`Process ${childProcess.pid} started`, MessageKind.DEBUG, { + notify(`Process ${childProcess.pid} started`, MessageKind.DEBUG, { logger, }); } @@ -55,7 +54,7 @@ export async function checkOpenSslInstalled(): Promise { const matcher = /(\d+.\d+.\d+)/; const installedVersion = result.cmdOutput.match(matcher); - showMessage( + notify( `Detected version ${installedVersion} of OpenSSL installed.`, MessageKind.DEBUG, { logger }, @@ -66,7 +65,9 @@ export async function checkOpenSslInstalled(): Promise { } catch (err) { // Disabled the error, as it is not critical // kdbOutputLog(`Error in checking OpenSSL version: ${err}`, "ERROR"); - Telemetry.sendException(err as Error); + notify("OpenSSL not found.", MessageKind.DEBUG, { + telemetry: err as Error, + }); } return null; } @@ -157,7 +158,7 @@ export function saveLocalProcessObj( childProcess: ChildProcess, args: string[], ): void { - showMessage( + notify( `Child process id ${childProcess.pid} saved in cache.`, MessageKind.DEBUG, { logger }, @@ -325,22 +326,24 @@ export function getServerAlias(serverList: ServerDetails[]): void { } export function tokenUndefinedError(connLabel: string): void { - showMessage( + notify( `Error retrieving access token for Insights connection named: ${connLabel}`, MessageKind.ERROR, + { logger }, ); } export function invalidUsernameJWT(connLabel: string): void { - showMessage( + notify( `JWT did not contain a valid preferred username for Insights connection: ${connLabel}`, MessageKind.ERROR, + { logger }, ); } /* istanbul ignore next */ export function offerConnectAction(connLabel: string): void { - showMessage( + notify( `You aren't connected to ${connLabel}, would you like to connect? Once connected please try again.`, MessageKind.INFO, {}, @@ -357,7 +360,7 @@ export function offerConnectAction(connLabel: string): void { } export function noSelectedConnectionAction(): void { - showMessage( + notify( `You didn't selected any existing connection to execute this action, please select a connection and try again.`, MessageKind.INFO, ); @@ -365,7 +368,7 @@ export function noSelectedConnectionAction(): void { /* istanbul ignore next */ export function offerReconnectionAfterEdit(connLabel: string): void { - showMessage( + notify( `You are no longer connected to ${connLabel}, would you like to connect?`, MessageKind.INFO, {}, @@ -437,7 +440,7 @@ export async function checkLocalInstall( if (QHOME || env.QHOME) { env.QHOME = QHOME || env.QHOME; if (!pathExists(env.QHOME!)) { - showMessage("QHOME path stored is empty.", MessageKind.ERROR); + notify("QHOME path stored is empty.", MessageKind.ERROR, { logger }); } await writeFile( join(__dirname, "qinstall.md"), @@ -449,11 +452,9 @@ export async function checkLocalInstall( .getConfiguration() .update("kdb.qHomeDirectory", env.QHOME, ConfigurationTarget.Global); - showMessage( - `Installation of q found here: ${env.QHOME}`, - MessageKind.DEBUG, - { logger }, - ); + notify(`Installation of q found here: ${env.QHOME}`, MessageKind.DEBUG, { + logger, + }); showRegistrationNotification(); @@ -461,10 +462,7 @@ export async function checkLocalInstall( .getConfiguration() .get("kdb.hideInstallationNotification"); if (!hideNotification) { - showMessage( - `Installation of q found here: ${env.QHOME}`, - MessageKind.INFO, - ); + notify(`Installation of q found here: ${env.QHOME}`, MessageKind.INFO); } // persist the notification seen option @@ -482,7 +480,7 @@ export async function checkLocalInstall( // set custom context that QHOME is not setup to control walkthrough visibility commands.executeCommand("setContext", "kdb.showInstallWalkthrough", true); - showMessage( + notify( "Local q installation not found!", MessageKind.INFO, {}, @@ -641,7 +639,7 @@ export function hasWorkspaceOrShowOption(action: string) { if (workspace.workspaceFolders && workspace.workspaceFolders.length > 0) { return true; } - showMessage( + notify( `No workspace folder is open. Please open a folder to enable ${action}.`, MessageKind.WARNING, {}, diff --git a/src/utils/cpUtils.ts b/src/utils/cpUtils.ts index e17ecdf8..2b9a8c89 100644 --- a/src/utils/cpUtils.ts +++ b/src/utils/cpUtils.ts @@ -16,7 +16,7 @@ import * as os from "os"; import { join } from "path"; import { ext } from "../extensionVariables"; -import { MessageKind, showMessage } from "./notifications"; +import { MessageKind, notify } from "./notifications"; const logger = "cpUtils"; @@ -38,7 +38,7 @@ export async function executeCommand( `Failed to run ${command} command. Check output window for more details.`, ); } else { - showMessage( + notify( `Finished running command: ${command} ${result.formattedArgs}`, MessageKind.DEBUG, { logger }, @@ -92,17 +92,17 @@ export async function tryExecuteCommand( data = data.toString(); cmdOutput = cmdOutput.concat(data); cmdOutputIncludingStderr = cmdOutputIncludingStderr.concat(data); - showMessage(data, MessageKind.DEBUG, { logger }); + notify(data, MessageKind.DEBUG, { logger }); }); childProc.stderr?.on("data", (data: string | Buffer) => { data = data.toString(); cmdOutputIncludingStderr = cmdOutputIncludingStderr.concat(data); - showMessage(data, MessageKind.DEBUG, { logger }); + notify(data, MessageKind.DEBUG, { logger }); }); childProc.on("error", (error) => { - showMessage(error.message, MessageKind.ERROR); + notify(error.message, MessageKind.ERROR, { logger }); reject(error); }); diff --git a/src/utils/dataSource.ts b/src/utils/dataSource.ts index 744d2c8f..2178822c 100644 --- a/src/utils/dataSource.ts +++ b/src/utils/dataSource.ts @@ -17,7 +17,7 @@ import { workspace, Uri } from "vscode"; import { InsightsConnection } from "../classes/insightsConnection"; import { ext } from "../extensionVariables"; -import { MessageKind, showMessage } from "./notifications"; +import { MessageKind, notify } from "./notifications"; import { Telemetry } from "./telemetryClient"; import { DataSourceFiles } from "../models/dataSource"; import { DataSourcesPanel } from "../panels/datasource"; @@ -28,7 +28,7 @@ export function createKdbDataSourcesFolder(): string { const rootPath = ext.context.globalStorageUri.fsPath; const kdbDataSourcesFolderPath = path.join(rootPath, ext.kdbDataSourceFolder); if (!fs.existsSync(rootPath)) { - showMessage( + notify( `Directory created to the extension folder: ${rootPath}`, MessageKind.DEBUG, { logger }, @@ -36,7 +36,7 @@ export function createKdbDataSourcesFolder(): string { fs.mkdirSync(rootPath); } if (!fs.existsSync(kdbDataSourcesFolderPath)) { - showMessage( + notify( `Directory created to the extension folder: ${kdbDataSourcesFolderPath}`, MessageKind.DEBUG, { logger }, @@ -55,11 +55,10 @@ export function convertTimeToTimestamp(time: string): string { const timePart = parts[1].replace("Z", "0").padEnd(9, "0"); return `${datePart}.${timePart}`; } catch (error) { - showMessage( - "The string param is in an incorrect format.", - MessageKind.ERROR, - { logger, params: { time, error } }, - ); + notify("The string param is in an incorrect format.", MessageKind.ERROR, { + logger, + params: { time, error }, + }); return ""; } } @@ -153,7 +152,8 @@ export async function addDSToLocalFolder(ds: DataSourceFiles): Promise { filePath = path.join(importToUri.fsPath, fileName); } fs.writeFileSync(filePath, JSON.stringify(ds)); - showMessage(`Datasource created.`, MessageKind.INFO); - Telemetry.sendEvent("Datasource.Created"); + notify(`Datasource created.`, MessageKind.INFO, { + telemetry: "Datasource.Created", + }); } } diff --git a/src/utils/execution.ts b/src/utils/execution.ts index cc642d7f..a9123564 100644 --- a/src/utils/execution.ts +++ b/src/utils/execution.ts @@ -16,7 +16,7 @@ import path from "path"; import { Uri, window, workspace } from "vscode"; import { ext } from "../extensionVariables"; -import { MessageKind, showMessage } from "./notifications"; +import { MessageKind, notify } from "./notifications"; import { QueryResultType } from "../models/queryResult"; const logger = "execution"; @@ -132,13 +132,13 @@ export async function exportToCsv(workspaceUri: Uri): Promise { try { await workspace.fs.writeFile(filePath, Buffer.from(ext.resultPanelCSV)); - showMessage("file located at: " + filePath.fsPath, MessageKind.DEBUG, { + notify("file located at: " + filePath.fsPath, MessageKind.DEBUG, { logger, }); window.showTextDocument(filePath, { preview: false }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); - showMessage(`Failed to write file: ${errorMessage}`, MessageKind.ERROR, { + notify(`Failed to write file: ${errorMessage}`, MessageKind.ERROR, { logger, }); } diff --git a/src/utils/executionConsole.ts b/src/utils/executionConsole.ts index 00669442..43bc04bc 100644 --- a/src/utils/executionConsole.ts +++ b/src/utils/executionConsole.ts @@ -18,7 +18,7 @@ import { getHideDetailedConsoleQueryOutput, setOutputWordWrapper, } from "./core"; -import { MessageKind, showMessage } from "./notifications"; +import { MessageKind, notify } from "./notifications"; import { addQueryHistory, checkIfIsDatasource, @@ -26,6 +26,8 @@ import { } from "./queryUtils"; import { ServerType } from "../models/connectionsModels"; +const logger = "executionConsole"; + export class ExecutionConsole { public static current: ExecutionConsole | undefined; private _console: OutputChannel; @@ -181,10 +183,9 @@ export class ExecutionConsole { ); } } else { - showMessage( - `Please connect to a KDB or Insights server`, - MessageKind.ERROR, - ); + notify(`Please connect to a KDB or Insights server`, MessageKind.ERROR, { + logger, + }); this._console.appendLine(`Please connect to a KDB or Insights server`); commands.executeCommand("kdb.connections.disconnect"); addQueryHistory( diff --git a/src/utils/feedbackSurveyUtils.ts b/src/utils/feedbackSurveyUtils.ts index fe62194a..88abd22e 100644 --- a/src/utils/feedbackSurveyUtils.ts +++ b/src/utils/feedbackSurveyUtils.ts @@ -14,7 +14,7 @@ import * as vscode from "vscode"; import { ext } from "../extensionVariables"; -import { MessageKind, showMessage } from "./notifications"; +import { MessageKind, notify } from "./notifications"; export async function feedbackSurveyDialog( sawSurveyAlready: boolean, @@ -48,7 +48,7 @@ export async function feedbackSurveyDialog( async function showSurveyDialog() { const SURVEY_URL = ext.urlLinks.survey; - const result = await showMessage( + const result = await notify( "Got 2 Minutes? Help us make the KX extension even better for your workflows.", MessageKind.INFO, {}, diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index 12d9bd6c..acba062c 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -13,8 +13,9 @@ import * as vscode from "vscode"; -import { kdbOutputLog } from "./loggers"; import { ext } from "../extensionVariables"; +import { kdbOutputLog } from "./loggers"; +import { Telemetry } from "./telemetryClient"; const logger = "notifications"; @@ -58,7 +59,7 @@ export class Runner { const updateCancelled = () => { this._cancelled = token.isCancellationRequested; if (this._cancelled) { - showMessage(`${this.title} cancelled.`, MessageKind.DEBUG, { + notify(`${this.title} cancelled.`, MessageKind.DEBUG, { logger, }); reject(new vscode.CancellationError()); @@ -96,22 +97,37 @@ export const enum MessageKind { ERROR = "ERROR", } -export function showMessage( +export function notify( message: string, kind: MessageKind, - options: { logger?: string; params?: any } = {}, + options: { + logger?: string; + params?: any; + telemetry?: string | boolean | Error; + } = {}, ...items: T[] ): Thenable { message = stripUnprintableChars(message); + if (options.logger) { const params = getParams(options.params); const log = `[${options.logger}] ${message} ${params}`.trim(); kdbOutputLog(log, kind, true); } + if (options.telemetry) { + if (typeof options.telemetry === "boolean") { + Telemetry.sendException(new Error(message)); + } else if (options.telemetry instanceof Error) { + Telemetry.sendException(options.telemetry); + } else { + Telemetry.sendEvent(options.telemetry); + } + } + let action: "Details" | "OK" | undefined; - if (items.length === 0) { + if (items.length === 0 && kind !== MessageKind.DEBUG) { action = options.params ? "Details" : "OK"; items.push(action); } diff --git a/src/utils/queryUtils.ts b/src/utils/queryUtils.ts index fe8a9484..cffe0af4 100644 --- a/src/utils/queryUtils.ts +++ b/src/utils/queryUtils.ts @@ -16,7 +16,7 @@ import { join } from "path"; import { ext } from "../extensionVariables"; import { isBaseVersionGreaterOrEqual } from "./core"; -import { MessageKind, showMessage } from "./notifications"; +import { MessageKind, notify } from "./notifications"; import { normalizeAssemblyTarget } from "./shared"; import { DCDS, deserialize, isCompressed, uncompress } from "../ipc/c"; import { DDateClass, DDateTimeClass, DTimestampClass } from "../ipc/cClasses"; @@ -99,7 +99,7 @@ export function handleWSError(ab: ArrayBuffer): any { } } - showMessage(`Error : ${errorString}`, MessageKind.DEBUG, { logger }); + notify(`Error : ${errorString}`, MessageKind.DEBUG, { logger }); return { error: errorString }; } diff --git a/src/utils/registration.ts b/src/utils/registration.ts index 0c2c3820..c3a7b110 100644 --- a/src/utils/registration.ts +++ b/src/utils/registration.ts @@ -14,7 +14,7 @@ import { ConfigurationTarget, workspace } from "vscode"; import { ext } from "../extensionVariables"; -import { MessageKind, showMessage } from "./notifications"; +import { MessageKind, notify } from "./notifications"; import { openUrl } from "./openUrl"; export function showRegistrationNotification(): void { @@ -22,7 +22,7 @@ export function showRegistrationNotification(): void { .getConfiguration() .get("kdb.hideSubscribeRegistrationNotification"); if (setting !== undefined && setting === false) { - showMessage( + notify( "Subscribe to updates", MessageKind.INFO, {}, diff --git a/src/utils/shell.ts b/src/utils/shell.ts index 56b4be7d..3ff0be04 100644 --- a/src/utils/shell.ts +++ b/src/utils/shell.ts @@ -14,14 +14,14 @@ import { ChildProcess } from "node:child_process"; import { ICommandResult, tryExecuteCommand } from "./cpUtils"; -import { MessageKind, showMessage } from "./notifications"; +import { MessageKind, notify } from "./notifications"; const logger = "shell"; const isWin = process.platform === "win32"; export function log(childProcess: ChildProcess): void { - showMessage(`Process ${childProcess.pid} killed`, MessageKind.DEBUG, { + notify(`Process ${childProcess.pid} killed`, MessageKind.DEBUG, { logger, }); } @@ -37,7 +37,7 @@ export async function killPid(pid = NaN): Promise { } else if (process.platform === "darwin") { result = await tryExecuteCommand("/bin", killPidCommand(pid), log); } - showMessage(`Destroying q process result: ${result}`, MessageKind.DEBUG, { + notify(`Destroying q process result: ${result}`, MessageKind.DEBUG, { logger, }); } From 0655f8df8d155414a57ef6ebb4a9aafa415731ea Mon Sep 17 00:00:00 2001 From: ecmel Date: Wed, 25 Jun 2025 08:07:36 +0300 Subject: [PATCH 05/10] added more params --- src/classes/localConnection.ts | 10 ++-------- src/commands/serverCommand.ts | 15 ++++++++++++--- src/services/connectionManagerService.ts | 1 + src/utils/core.ts | 7 +++++-- src/utils/dataSource.ts | 2 +- src/utils/notifications.ts | 8 ++++---- 6 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/classes/localConnection.ts b/src/classes/localConnection.ts index 9b169975..b7cca541 100644 --- a/src/classes/localConnection.ts +++ b/src/classes/localConnection.ts @@ -90,7 +90,7 @@ export class LocalConnection { notify( `Connection to server ${this.options.host}:${this.options.port} failed.`, MessageKind.ERROR, - {}, + { logger, params: err }, "Start q process", ).then((res) => { if (res) { @@ -106,16 +106,10 @@ export class LocalConnection { notify( `Connection to server ${this.options.host}:${this.options.port} failed.`, MessageKind.ERROR, - { logger }, + { logger, params: err }, ); } - notify( - `Connection to server ${this.options.host}:${this.options.port} failed.`, - MessageKind.ERROR, - { logger, params: err }, - ); - return; } conn.addListener("close", () => { diff --git a/src/commands/serverCommand.ts b/src/commands/serverCommand.ts index 28cc95f8..d8e023e0 100644 --- a/src/commands/serverCommand.ts +++ b/src/commands/serverCommand.ts @@ -170,6 +170,7 @@ export async function addInsightsConnection( } ext.serverProvider.refreshInsights(newInsights); notify("Created Insights connection.", MessageKind.DEBUG, { + logger, telemetry: "Connection.Created.Insights", }); } @@ -177,6 +178,7 @@ export async function addInsightsConnection( notify( `Added Insights connection: ${insightsData.alias}`, MessageKind.INFO, + { logger }, ); NewConnectionPannel.close(); @@ -269,6 +271,7 @@ export async function editInsightsConnection( } ext.serverProvider.refreshInsights(newInsights); notify("Edited Insights connection.", MessageKind.DEBUG, { + logger, telemetry: "Connection.Edited.Insights", }); if (isConnectedConn) { @@ -279,6 +282,7 @@ export async function editInsightsConnection( notify( `Edited Insights connection: ${insightsData.alias}`, MessageKind.INFO, + { logger }, ); NewConnectionPannel.close(); @@ -438,6 +442,7 @@ export async function addKdbConnection( await handleLabelsConnMap(labels, kdbData.serverAlias); } notify("Created kdb connection.", MessageKind.DEBUG, { + logger, telemetry: "Connection.Created.QProcess", }); ext.serverProvider.refresh(newServers); @@ -446,7 +451,9 @@ export async function addKdbConnection( addAuthConnection(key, kdbData.username!, kdbData.password!); } - notify(`Added kdb connection: ${kdbData.serverAlias}`, MessageKind.INFO); + notify(`Added kdb connection: ${kdbData.serverAlias}`, MessageKind.INFO, { + logger, + }); NewConnectionPannel.close(); } @@ -551,6 +558,7 @@ export async function editKdbConnection( } ext.serverProvider.refresh(newServers); notify("Edited kdb connection.", MessageKind.DEBUG, { + logger, telemetry: "Connection.Edited.KDB", }); const connLabelToReconn = `${kdbData.serverName}:${kdbData.serverPort} [${kdbData.serverAlias}]`; @@ -562,6 +570,7 @@ export async function editKdbConnection( notify( `Edited KDB connection: ${kdbData.serverAlias}`, MessageKind.INFO, + { logger }, ); if (oldKey !== newKey) { removeConnFromLabels(oldKey); @@ -762,7 +771,7 @@ export async function connect(connLabel: string): Promise { notify( "TLS support requires OpenSSL to be installed.", MessageKind.INFO, - {}, + { logger }, "More Info", "Cancel", ).then(async (result) => { @@ -1117,7 +1126,7 @@ export function copyQuery(queryHistoryElement: QueryHistory) { typeof queryHistoryElement.query === "string" ) { env.clipboard.writeText(queryHistoryElement.query); - notify("Query copied to clipboard.", MessageKind.INFO); + notify("Query copied to clipboard.", MessageKind.INFO, { logger }); } } diff --git a/src/services/connectionManagerService.ts b/src/services/connectionManagerService.ts index 646214ea..b706ce8c 100644 --- a/src/services/connectionManagerService.ts +++ b/src/services/connectionManagerService.ts @@ -200,6 +200,7 @@ export class ConnectionManagementService { `${node.label}`, ]); notify("Connection activated.", MessageKind.DEBUG, { + logger, telemetry: "Connection.Connected.Active", }); ext.activeConnection = connection; diff --git a/src/utils/core.ts b/src/utils/core.ts index 830e32b4..a3ed24f5 100644 --- a/src/utils/core.ts +++ b/src/utils/core.ts @@ -66,6 +66,7 @@ export async function checkOpenSslInstalled(): Promise { // Disabled the error, as it is not critical // kdbOutputLog(`Error in checking OpenSSL version: ${err}`, "ERROR"); notify("OpenSSL not found.", MessageKind.DEBUG, { + logger, telemetry: err as Error, }); } @@ -462,7 +463,9 @@ export async function checkLocalInstall( .getConfiguration() .get("kdb.hideInstallationNotification"); if (!hideNotification) { - notify(`Installation of q found here: ${env.QHOME}`, MessageKind.INFO); + notify(`Installation of q found here: ${env.QHOME}`, MessageKind.INFO, { + logger, + }); } // persist the notification seen option @@ -483,7 +486,7 @@ export async function checkLocalInstall( notify( "Local q installation not found!", MessageKind.INFO, - {}, + { logger }, "Install new instance", "No", "Never show again", diff --git a/src/utils/dataSource.ts b/src/utils/dataSource.ts index 2178822c..5b460a25 100644 --- a/src/utils/dataSource.ts +++ b/src/utils/dataSource.ts @@ -18,7 +18,6 @@ import { workspace, Uri } from "vscode"; import { InsightsConnection } from "../classes/insightsConnection"; import { ext } from "../extensionVariables"; import { MessageKind, notify } from "./notifications"; -import { Telemetry } from "./telemetryClient"; import { DataSourceFiles } from "../models/dataSource"; import { DataSourcesPanel } from "../panels/datasource"; @@ -153,6 +152,7 @@ export async function addDSToLocalFolder(ds: DataSourceFiles): Promise { } fs.writeFileSync(filePath, JSON.stringify(ds)); notify(`Datasource created.`, MessageKind.INFO, { + logger, telemetry: "Datasource.Created", }); } diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index acba062c..7805fdfa 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -132,7 +132,7 @@ export function notify( items.push(action); } - const dialog = + const notification = kind === MessageKind.ERROR ? vscode.window.showErrorMessage(message, ...items) : kind === MessageKind.WARNING @@ -142,17 +142,17 @@ export function notify( : Promise.resolve(undefined); if (action === "Details") { - dialog.then((res) => { + notification.then((res) => { if (res === "Details") { ext.outputChannel.show(true); } }); } - return dialog; + return notification; } -function getParams(params?: Array) { +function getParams(params?: any) { if (params) { try { return JSON.stringify(params); From 7b9f0c9427168f1d13bf04f645d86558f131e667 Mon Sep 17 00:00:00 2001 From: ecmel Date: Wed, 25 Jun 2025 16:18:16 +0300 Subject: [PATCH 06/10] continue merge --- src/classes/insightsConnection.ts | 59 +++++++++++++----------- src/commands/dataSourceCommand.ts | 7 ++- src/commands/serverCommand.ts | 26 +++++++---- src/services/connectionManagerService.ts | 24 +++++----- src/utils/connLabel.ts | 2 +- src/utils/feedbackSurveyUtils.ts | 1 + 6 files changed, 70 insertions(+), 49 deletions(-) diff --git a/src/classes/insightsConnection.ts b/src/classes/insightsConnection.ts index 42039a82..fea5f8e1 100644 --- a/src/classes/insightsConnection.ts +++ b/src/classes/insightsConnection.ts @@ -14,6 +14,7 @@ import axios, { AxiosRequestConfig } from "axios"; import { jwtDecode } from "jwt-decode"; import * as url from "url"; +import { ProgressLocation, window } from "vscode"; import { ext } from "../extensionVariables"; import { isCompressed, uncompress } from "../ipc/c"; @@ -390,16 +391,19 @@ export class InsightsConnection { }, async (progress, token) => { token.onCancellationRequested(() => { - kdbOutputLog(`User cancelled the Datasource Run.`, "WARNING"); + notify(`User cancelled the Datasource Run.`, MessageKind.DEBUG, { + logger, + }); }); progress.report({ message: "Query executing..." }); return await axios(options) .then((response: any) => { - kdbOutputLog( + notify( `[Datasource RUN] Status: ${response.status}.`, - "INFO", + MessageKind.DEBUG, + { logger }, ); if (isCompressed(response.data)) { response.data = uncompress(response.data); @@ -412,9 +416,10 @@ export class InsightsConnection { }; }) .catch((error: any) => { - kdbOutputLog( + notify( `[Datasource RUN] Status: ${error.response.status}.`, - "ERROR", + MessageKind.ERROR, + { logger, params: error }, ); return { error: { buffer: error.response.data }, @@ -514,37 +519,37 @@ export class InsightsConnection { }, async (progress, token) => { token.onCancellationRequested(() => { - kdbOutputLog(`User cancelled the scratchpad import.`, "WARNING"); + notify(`User cancelled the scratchpad import.`, MessageKind.DEBUG, { + logger, + }); }); progress.report({ message: "Populating scratchpad..." }); return await axios(options).then((response: any) => { if (response.data.error) { - kdbOutputLog( - `[SCRATCHPAD] Error occured while populating scratchpad: ${response.data.errorMsg}`, - "ERROR", - ); - Telemetry.sendEvent( - "Datasource." + dsTypeString + ".Scratchpad.Populated.Errored", + notify( + "Error occured while populating scratchpad.", + MessageKind.ERROR, + { + logger, + params: response.data.errorMsg, + telemetry: + "Datasource." + + dsTypeString + + ".Scratchpad.Populated.Errored", + }, ); } else { - kdbOutputLog( + notify( `Executed successfully, stored in ${variableName}.`, - "INFO", - ); - kdbOutputLog(`[SCRATCHPAD] Status: ${response.status}`, "INFO"); - kdbOutputLog( - `[SCRATCHPAD] Populated scratchpad with the following params: ${JSON.stringify( - body.params, - )}`, - "INFO", - ); - window.showInformationMessage( - `Executed successfully, stored in ${variableName}.`, - ); - Telemetry.sendEvent( - "Datasource." + dsTypeString + ".Scratchpad.Populated", + MessageKind.INFO, + { + logger, + params: { status: response.status, params: body.params }, + telemetry: + "Datasource." + dsTypeString + ".Scratchpad.Populated", + }, ); } }); diff --git a/src/commands/dataSourceCommand.ts b/src/commands/dataSourceCommand.ts index eee49a62..48e9608f 100644 --- a/src/commands/dataSourceCommand.ts +++ b/src/commands/dataSourceCommand.ts @@ -190,8 +190,11 @@ export async function runDataSource( const query = getQuery(fileContent, selectedType); if (!success) { - Telemetry.sendEvent("Datasource." + selectedType + ".Run.Error"); - window.showErrorMessage(res.error); + notify("Query execution failed.", MessageKind.ERROR, { + logger, + params: res.error, + telemetry: "Datasource." + selectedType + ".Run.Error", + }); } if (ext.isResultsTabVisible) { if (success) { diff --git a/src/commands/serverCommand.ts b/src/commands/serverCommand.ts index 397ec76c..919c7914 100644 --- a/src/commands/serverCommand.ts +++ b/src/commands/serverCommand.ts @@ -79,6 +79,7 @@ import { formatScratchpadStacktrace, resultToBase64, } from "../utils/queryUtils"; +import { Telemetry } from "../utils/telemetryClient"; import { addWorkspaceFile, openWith, @@ -888,11 +889,15 @@ export async function _executeQuery( const connVersion = isInsights ? (selectedConn.insightsVersion ?? 0) : 0; const telemetryLangType = isPython ? ".Python" : ".q"; const telemetryBaseMsg = isWorkbook ? "Workbook" : "Scratchpad"; - Telemetry.sendEvent(telemetryBaseMsg + ".Execute" + telemetryLangType); + notify("Query execution.", MessageKind.DEBUG, { + logger, + telemetry: telemetryBaseMsg + ".Execute" + telemetryLangType, + }); if (query.length === 0) { - Telemetry.sendEvent( - telemetryBaseMsg + ".Execute" + telemetryLangType + ".Error", - ); + notify("Empty query.", MessageKind.DEBUG, { + logger, + telemetry: telemetryBaseMsg + ".Execute" + telemetryLangType + ".Error", + }); queryConsole.appendQueryError( query, "Query is empty", @@ -942,7 +947,10 @@ export async function _executeQuery( if (ext.isResultsTabVisible) { const data = resultToBase64(results); if (data) { - Telemetry.sendEvent("GGPLOT.Display" + (isPython ? ".Python" : ".q")); + notify("GG Plot displayed", MessageKind.DEBUG, { + logger, + telemetry: "GGPLOT.Display" + (isPython ? ".Python" : ".q"), + }); const active = ext.activeTextEditor; if (active) { const plot = { @@ -1313,9 +1321,11 @@ export async function writeQueryResultsToView( if (typeof result === "string") { const res = decodeQUTF(result); if (res.startsWith(queryConstants.error)) { - Telemetry.sendEvent( - telemetryBaseMsg + ".Execute" + telemetryLangType + ".Error", - ); + notify("Telemetry", MessageKind.DEBUG, { + logger, + telemetry: + telemetryBaseMsg + ".Execute" + telemetryLangType + ".Error", + }); isSuccess = false; } } diff --git a/src/services/connectionManagerService.ts b/src/services/connectionManagerService.ts index bda419b6..2f6e5a11 100644 --- a/src/services/connectionManagerService.ts +++ b/src/services/connectionManagerService.ts @@ -38,6 +38,7 @@ import { import { refreshDataSourcesPanel } from "../utils/dataSource"; import { MessageKind, notify } from "../utils/notifications"; import { sanitizeQuery } from "../utils/queryUtils"; +import { Telemetry } from "../utils/telemetryClient"; const logger = "connectionManagerService"; @@ -145,7 +146,6 @@ export class ConnectionManagementService { ); await localConnection.connect((err, conn) => { if (err) { - window.showErrorMessage(err.message); this.connectFailBehaviour(connLabel); return; } @@ -153,11 +153,12 @@ export class ConnectionManagementService { notify( `Connection established successfully to: ${connLabel}`, MessageKind.DEBUG, - { logger, telemetry: "Connection.Connected.QProcess" }, - ); - - Telemetry.sendEvent( - "Connection.Connected" + this.getTelemetryConnectionType(connLabel), + { + logger, + telemetry: + "Connection.Connected" + + this.getTelemetryConnectionType(connLabel), + }, ); ext.connectedConnectionList.push(localConnection); @@ -176,7 +177,7 @@ export class ConnectionManagementService { Telemetry.sendEvent( "Connection.Connected" + this.getTelemetryConnectionType(connLabel), ); - kdbOutputLog( + notify( `Connection established successfully to: ${connLabel}`, MessageKind.DEBUG, { logger, telemetry: "Connection.Connected.Insights" }, @@ -299,10 +300,11 @@ export class ConnectionManagementService { } public connectFailBehaviour(connLabel: string): void { - window.showErrorMessage(`Connection failed to: ${connLabel}`); - Telemetry.sendEvent( - "Connection.Failed" + this.getTelemetryConnectionType(connLabel), - ); + notify(`Connection failed to: ${connLabel}`, MessageKind.ERROR, { + logger, + telemetry: + "Connection.Failed" + this.getTelemetryConnectionType(connLabel), + }); } public disconnectBehaviour( diff --git a/src/utils/connLabel.ts b/src/utils/connLabel.ts index 166c167e..06a81620 100644 --- a/src/utils/connLabel.ts +++ b/src/utils/connLabel.ts @@ -14,7 +14,7 @@ import { workspace } from "vscode"; import { ext } from "../extensionVariables"; -import { kdbOutputLog } from "./core"; +import { MessageKind, notify } from "./notifications"; import { Telemetry } from "./telemetryClient"; import { ConnectionLabel, Labels } from "../models/labels"; import { NewConnectionPannel } from "../panels/newConnection"; diff --git a/src/utils/feedbackSurveyUtils.ts b/src/utils/feedbackSurveyUtils.ts index f6bca768..2c15b67a 100644 --- a/src/utils/feedbackSurveyUtils.ts +++ b/src/utils/feedbackSurveyUtils.ts @@ -14,6 +14,7 @@ import * as vscode from "vscode"; import { ext } from "../extensionVariables"; +import { MessageKind, notify } from "./notifications"; import { Telemetry } from "./telemetryClient"; export async function feedbackSurveyDialog( From 6d4a477380b4be544d2b184ec6ac5aeb6b890985 Mon Sep 17 00:00:00 2001 From: ecmel Date: Thu, 26 Jun 2025 09:30:50 +0300 Subject: [PATCH 07/10] continue merge --- src/classes/insightsConnection.ts | 263 +++++++++++------------ src/commands/serverCommand.ts | 9 +- src/commands/workspaceCommand.ts | 30 ++- src/extension.ts | 41 +++- src/services/connectionManagerService.ts | 17 +- src/utils/connLabel.ts | 36 ++-- src/utils/feedbackSurveyUtils.ts | 9 +- src/utils/notifications.ts | 8 +- src/utils/workspace.ts | 10 +- 9 files changed, 227 insertions(+), 196 deletions(-) diff --git a/src/classes/insightsConnection.ts b/src/classes/insightsConnection.ts index 8891fee0..23a32217 100644 --- a/src/classes/insightsConnection.ts +++ b/src/classes/insightsConnection.ts @@ -14,7 +14,6 @@ import axios, { AxiosRequestConfig } from "axios"; import { jwtDecode } from "jwt-decode"; import * as url from "url"; -import { ProgressLocation, window } from "vscode"; import { ext } from "../extensionVariables"; import { isCompressed, uncompress } from "../ipc/c"; @@ -384,51 +383,46 @@ export class InsightsConnection { return undefined; } options.responseType = "arraybuffer"; - const results = await window.withProgress( - { - location: ProgressLocation.Notification, - cancellable: true, - }, - async (progress, token) => { - token.onCancellationRequested(() => { - notify(`User cancelled the Datasource Run.`, MessageKind.DEBUG, { - logger, - }); + const runner = Runner.create(async (progress, token) => { + token.onCancellationRequested(() => { + notify(`User cancelled the Datasource Run.`, MessageKind.DEBUG, { + logger, }); + }); - progress.report({ message: "Query executing..." }); - - return await axios(options) - .then((response: any) => { - notify( - `[Datasource RUN] Status: ${response.status}.`, - MessageKind.DEBUG, - { logger }, - ); - if (isCompressed(response.data)) { - response.data = uncompress(response.data); - } - return { - error: "", - arrayBuffer: response.data.buffer - ? response.data.buffer - : response.data, - }; - }) - .catch((error: any) => { - notify( - `[Datasource RUN] Status: ${error.response.status}.`, - MessageKind.ERROR, - { logger, params: error }, - ); - return { - error: { buffer: error.response.data }, - arrayBuffer: undefined, - }; - }); - }, - ); - return results; + progress.report({ message: "Query executing..." }); + + return await axios(options) + .then((response: any) => { + notify( + `[Datasource RUN] Status: ${response.status}.`, + MessageKind.DEBUG, + { logger }, + ); + if (isCompressed(response.data)) { + response.data = uncompress(response.data); + } + return { + error: "", + arrayBuffer: response.data.buffer + ? response.data.buffer + : response.data, + }; + }) + .catch((error: any) => { + notify( + `[Datasource RUN] Status: ${error.response.status}.`, + MessageKind.ERROR, + { logger, params: error }, + ); + return { + error: { buffer: error.response.data }, + arrayBuffer: undefined, + }; + }); + }); + + return await runner.execute(); } } @@ -512,49 +506,44 @@ export class InsightsConnection { return; } - await window.withProgress( - { - location: ProgressLocation.Notification, - cancellable: false, - }, - async (progress, token) => { - token.onCancellationRequested(() => { - notify(`User cancelled the scratchpad import.`, MessageKind.DEBUG, { - logger, - }); + const runner = Runner.create(async (progress, token) => { + token.onCancellationRequested(() => { + notify(`User cancelled the scratchpad import.`, MessageKind.DEBUG, { + logger, }); + }); - progress.report({ message: "Populating scratchpad..." }); - - return await axios(options).then((response: any) => { - if (response.data.error) { - notify( - "Error occured while populating scratchpad.", - MessageKind.ERROR, - { - logger, - params: response.data.errorMsg, - telemetry: - "Datasource." + - dsTypeString + - ".Scratchpad.Populated.Errored", - }, - ); - } else { - notify( - `Executed successfully, stored in ${variableName}.`, - MessageKind.INFO, - { - logger, - params: { status: response.status, params: body.params }, - telemetry: - "Datasource." + dsTypeString + ".Scratchpad.Populated", - }, - ); - } - }); - }, - ); + progress.report({ message: "Populating scratchpad..." }); + + return await axios(options).then((response: any) => { + if (response.data.error) { + notify( + "Error occured while populating scratchpad.", + MessageKind.ERROR, + { + logger, + params: response.data.errorMsg, + telemetry: + "Datasource." + + dsTypeString + + ".Scratchpad.Populated.Errored", + }, + ); + } else { + notify( + `Executed successfully, stored in ${variableName}.`, + MessageKind.INFO, + { + logger, + params: { status: response.status, params: body.params }, + telemetry: + "Datasource." + dsTypeString + ".Scratchpad.Populated", + }, + ); + } + }); + }); + await runner.execute(); } else { this.noConnectionOrEndpoints(); } @@ -687,65 +676,63 @@ export class InsightsConnection { return; } - const spResponse = await window.withProgress( - { - location: ProgressLocation.Notification, - cancellable: false, - }, - async (progress, token) => { - token.onCancellationRequested(() => { - kdbOutputLog(`User cancelled the scratchpad execution.`, "WARNING"); - }); + const runner = Runner.create(async (progress, token) => { + token.onCancellationRequested(() => { + notify( + `User cancelled the scratchpad execution.`, + MessageKind.DEBUG, + { logger }, + ); + }); - if (isStarting) { - progress.report({ message: "Starting scratchpad..." }); - } else { - progress.report({ message: "Query is running..." }); - } + if (isStarting) { + progress.report({ message: "Starting scratchpad..." }); + } else { + progress.report({ message: "Query is running..." }); + } - const spRes = await axios(options).then((response: any) => { - if (response.data.error) { - return response.data; - } else if (query === "") { - notify( - `Scratchpad created for connection: ${this.connLabel}.`, - MessageKind.DEBUG, - { logger }, - ); - } else { - notify(`Status: ${response.status}`, MessageKind.DEBUG, { - logger, - }); - if (!response.data.error) { - if (isTableView) { - if ( - /* TODO: Workaround for Python structuredText bug */ - !isPython && - this.insightsVersion && - isBaseVersionGreaterOrEqual(this.insightsVersion, 1.12) - ) { - response.data = JSON.parse( - response.data.data, - ) as StructuredTextResults; - } else { - const buffer = new Uint8Array( - response.data.data.map((x: string) => parseInt(x, 16)), - ).buffer; - - response.data.data = handleWSResults(buffer, isTableView); - response.data.data = handleScratchpadTableRes( - response.data.data, - ); - } + const spRes = await axios(options).then((response: any) => { + if (response.data.error) { + return response.data; + } else if (query === "") { + notify( + `Scratchpad created for connection: ${this.connLabel}.`, + MessageKind.DEBUG, + { logger }, + ); + } else { + notify(`Status: ${response.status}`, MessageKind.DEBUG, { + logger, + }); + if (!response.data.error) { + if (isTableView) { + if ( + /* TODO: Workaround for Python structuredText bug */ + !isPython && + this.insightsVersion && + isBaseVersionGreaterOrEqual(this.insightsVersion, 1.12) + ) { + response.data = JSON.parse( + response.data.data, + ) as StructuredTextResults; + } else { + const buffer = new Uint8Array( + response.data.data.map((x: string) => parseInt(x, 16)), + ).buffer; + + response.data.data = handleWSResults(buffer, isTableView); + response.data.data = handleScratchpadTableRes( + response.data.data, + ); } - return response.data; } return response.data; } - }); - return spRes; - }, - ); + return response.data; + } + }); + return spRes; + }); return await runner.execute(); } else { this.noConnectionOrEndpoints(); diff --git a/src/commands/serverCommand.ts b/src/commands/serverCommand.ts index 919c7914..452a409e 100644 --- a/src/commands/serverCommand.ts +++ b/src/commands/serverCommand.ts @@ -79,7 +79,6 @@ import { formatScratchpadStacktrace, resultToBase64, } from "../utils/queryUtils"; -import { Telemetry } from "../utils/telemetryClient"; import { addWorkspaceFile, openWith, @@ -1361,9 +1360,11 @@ export async function writeScratchpadResult( if (result.error) { errorMsg = "Error: " + result.errorMsg; - Telemetry.sendEvent( - telemetryBaseMsg + ".Execute" + telemetryLangType + ".Error", - ); + + notify("Scratchpad query returned error", MessageKind.DEBUG, { + logger, + telemetry: telemetryBaseMsg + ".Execute" + telemetryLangType + ".Error", + }); if (result.stacktrace) { errorMsg = diff --git a/src/commands/workspaceCommand.ts b/src/commands/workspaceCommand.ts index 5b13abea..db29f684 100644 --- a/src/commands/workspaceCommand.ts +++ b/src/commands/workspaceCommand.ts @@ -16,7 +16,6 @@ import { CodeLens, CodeLensProvider, Command, - ProgressLocation, Range, StatusBarAlignment, TextDocument, @@ -35,7 +34,7 @@ import { ConnectionManagementService } from "../services/connectionManagerServic import { InsightsNode, KdbNode, LabelNode } from "../services/kdbTreeProvider"; import { offerConnectAction } from "../utils/core"; import { importOldDsFiles, oldFilesExists } from "../utils/dataSource"; -import { MessageKind, notify } from "../utils/notifications"; +import { MessageKind, notify, Runner } from "../utils/notifications"; import { normalizeAssemblyTarget } from "../utils/shared"; const logger = "workspaceCommand"; @@ -410,24 +409,19 @@ export async function importOldDSFiles() { notify("No workspace folder found.", MessageKind.ERROR, { logger }); return; } - return await window.withProgress( - { - location: ProgressLocation.Notification, - cancellable: false, - }, - async (progress, token) => { - token.onCancellationRequested(() => { - notify("User cancelled the old DS files import.", MessageKind.DEBUG, { - logger, - }); - return false; + const runner = Runner.create(async (progress, token) => { + token.onCancellationRequested(() => { + notify("User cancelled the old DS files import.", MessageKind.DEBUG, { + logger, }); + return false; + }); - progress.report({ message: "Importing old DS files..." }); - await importOldDsFiles(); - return; - }, - ); + progress.report({ message: "Importing old DS files..." }); + await importOldDsFiles(); + return; + }); + return await runner.execute(); } else { notify("No old Datasource files found on your VSCODE.", MessageKind.INFO, { logger, diff --git a/src/extension.ts b/src/extension.ts index 11f30b87..858146d1 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -117,6 +117,7 @@ import { import { runQFileTerminal } from "./utils/execution"; import { handleFeedbackSurvey } from "./utils/feedbackSurveyUtils"; import { getIconPath } from "./utils/iconsUtils"; +import { MessageKind, notify } from "./utils/notifications"; import AuthSettings from "./utils/secretStorage"; import { Telemetry } from "./utils/telemetryClient"; import { @@ -344,7 +345,10 @@ export async function activate(context: vscode.ExtensionContext) { if (authExtension) { const api = await authExtension.activate(); if ("auth" in api) { - Telemetry.sendEvent("CustomAuth.Extension.Actived"); + notify("Custom authentication activated.", MessageKind.DEBUG, { + logger, + telemetry: "CustomAuth.Extension.Actived", + }); ext.customAuth = api; } } @@ -367,7 +371,10 @@ function registerHelpCommands(): CommandRegistration[] { "KX.kdb", ) .then(undefined, () => { - Telemetry.sendEvent("Help&Feedback.Open.ExtensionDocumentation"); + notify("Help&Feedback documentation selected.", MessageKind.DEBUG, { + logger, + telemetry: "Help&Feedback.Open.ExtensionDocumentation", + }); vscode.commands.executeCommand("extension.open", "KX.kdb"); }); }, @@ -375,21 +382,30 @@ function registerHelpCommands(): CommandRegistration[] { { command: "kdb.help.suggestFeature", callback: () => { - Telemetry.sendEvent("Help&Feedback.Open.SuggestFeature"); + notify("Help&Feedback suggest a feature selected.", MessageKind.DEBUG, { + logger, + telemetry: "Help&Feedback.Open.SuggestFeature", + }); vscode.env.openExternal(vscode.Uri.parse(ext.urlLinks.suggestFeature)); }, }, { command: "kdb.help.provideFeedback", callback: () => { - Telemetry.sendEvent("Help&Feedback.Open.Survey"); + notify("Help&Feedback survey selected.", MessageKind.DEBUG, { + logger, + telemetry: "Help&Feedback.Open.Survey", + }); vscode.env.openExternal(vscode.Uri.parse(ext.urlLinks.survey)); }, }, { command: "kdb.help.reportBug", callback: () => { - Telemetry.sendEvent("Help&Feedback.Open.ReportBug"); + notify("Help&Feedback report a bug selected.", MessageKind.DEBUG, { + logger, + telemetry: "Help&Feedback.Open.ReportBug", + }); vscode.env.openExternal(vscode.Uri.parse(ext.urlLinks.reportBug)); }, }, @@ -647,21 +663,30 @@ function registerConnectionsCommands(): CommandRegistration[] { { command: "kdb.connections.export.all", callback: () => { - Telemetry.sendEvent("Connections.Export.All"); + notify("Export all conections.", MessageKind.DEBUG, { + logger, + telemetry: "Connections.Export.All", + }); exportConnections(); }, }, { command: "kdb.connections.export.single", callback: async (viewItem: KdbNode | InsightsNode) => { - Telemetry.sendEvent("Connections.Export.Single"); + notify("Export single conection.", MessageKind.DEBUG, { + logger, + telemetry: "Connections.Export.Single", + }); exportConnections(viewItem.label); }, }, { command: "kdb.connections.import", callback: async () => { - Telemetry.sendEvent("Connections.Import"); + notify("Import conections.", MessageKind.DEBUG, { + logger, + telemetry: "Connections.Import", + }); await importConnections(); }, }, diff --git a/src/services/connectionManagerService.ts b/src/services/connectionManagerService.ts index 2f6e5a11..2ff283c1 100644 --- a/src/services/connectionManagerService.ts +++ b/src/services/connectionManagerService.ts @@ -38,7 +38,6 @@ import { import { refreshDataSourcesPanel } from "../utils/dataSource"; import { MessageKind, notify } from "../utils/notifications"; import { sanitizeQuery } from "../utils/queryUtils"; -import { Telemetry } from "../utils/telemetryClient"; const logger = "connectionManagerService"; @@ -174,18 +173,16 @@ export class ConnectionManagementService { ); await insightsConn.connect(); if (insightsConn.connected) { - Telemetry.sendEvent( - "Connection.Connected" + this.getTelemetryConnectionType(connLabel), - ); notify( `Connection established successfully to: ${connLabel}`, MessageKind.DEBUG, - { logger, telemetry: "Connection.Connected.Insights" }, - ); - notify( - `${connLabel} connection insights version: ${insightsConn.insightsVersion}`, - MessageKind.DEBUG, - { logger }, + { + logger, + params: { insightsVersion: insightsConn.insightsVersion }, + telemetry: + "Connection.Connected" + + this.getTelemetryConnectionType(connLabel), + }, ); ext.connectedConnectionList.push(insightsConn); this.connectSuccessBehaviour(connection); diff --git a/src/utils/connLabel.ts b/src/utils/connLabel.ts index 06a81620..86400d88 100644 --- a/src/utils/connLabel.ts +++ b/src/utils/connLabel.ts @@ -15,7 +15,6 @@ import { workspace } from "vscode"; import { ext } from "../extensionVariables"; import { MessageKind, notify } from "./notifications"; -import { Telemetry } from "./telemetryClient"; import { ConnectionLabel, Labels } from "../models/labels"; import { NewConnectionPannel } from "../panels/newConnection"; import { InsightsNode, KdbNode } from "../services/kdbTreeProvider"; @@ -51,7 +50,12 @@ export function createNewLabel(name: string, colorName: string) { workspace .getConfiguration() .update("kdb.connectionLabels", ext.connLabelList, true); - Telemetry.sendEvent("Label.Create", {}, getLabelStatistics()); + + notify("Connection label created.", MessageKind.DEBUG, { + logger, + telemetry: "Label.Create", + measurements: getLabelStatistics(), + }); } else { notify("No Color selected for the label.", MessageKind.ERROR, { logger, @@ -96,11 +100,11 @@ export function addConnToLabel(labelName: string, connName: string) { connections: [connName], }); } - Telemetry.sendEvent( - "Label.Assign.Connection", - {}, - getConnectionLabelStatistics(connName), - ); + notify("Connection assigned to label.", MessageKind.DEBUG, { + logger, + telemetry: "Label.Assign.Connection", + measurements: getConnectionLabelStatistics(connName), + }); } } @@ -115,11 +119,12 @@ export function removeConnFromLabels(connName: string) { workspace .getConfiguration() .update("kdb.labelsConnectionMap", ext.labelConnMapList, true); - Telemetry.sendEvent( - "Label.Remove.Connection", - {}, - getConnectionLabelStatistics(connName), - ); + + notify("Connection removed from label.", MessageKind.DEBUG, { + logger, + telemetry: "Label.Remove.Connection", + measurements: getConnectionLabelStatistics(connName), + }); } export async function handleLabelsConnMap(labels: string[], connName: string) { @@ -192,7 +197,12 @@ export function deleteLabel(name: string) { workspace .getConfiguration() .update("kdb.connectionLabels", ext.connLabelList, true); - Telemetry.sendEvent("Label.Delete", {}, getLabelStatistics()); + + notify("Connection label deleted.", MessageKind.DEBUG, { + logger, + telemetry: "Label.Delete", + measurements: getLabelStatistics(), + }); NewConnectionPannel.refreshLabels(); } diff --git a/src/utils/feedbackSurveyUtils.ts b/src/utils/feedbackSurveyUtils.ts index 2c15b67a..aefbb7b7 100644 --- a/src/utils/feedbackSurveyUtils.ts +++ b/src/utils/feedbackSurveyUtils.ts @@ -15,7 +15,8 @@ import * as vscode from "vscode"; import { ext } from "../extensionVariables"; import { MessageKind, notify } from "./notifications"; -import { Telemetry } from "./telemetryClient"; + +const logger = "feedbackSurveyUtils"; export async function feedbackSurveyDialog( sawSurveyAlready: boolean, @@ -59,7 +60,11 @@ async function showSurveyDialog() { if (result === "Take Survey") { vscode.env.openExternal(vscode.Uri.parse(SURVEY_URL)); } else if (result === "Don't show me this message next time") { - Telemetry.sendEvent("Help&Feedback.Hide.Survey"); + notify("Take survey message silenced.", MessageKind.DEBUG, { + logger, + telemetry: "Help&Feedback.Hide.Survey", + }); + await vscode.workspace .getConfiguration("kdb") .update("hideSurvey", true, vscode.ConfigurationTarget.Global); diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index 7805fdfa..fdb2760f 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -104,6 +104,8 @@ export function notify( logger?: string; params?: any; telemetry?: string | boolean | Error; + properties?: { [key: string]: string }; + measurements?: { [key: string]: number }; } = {}, ...items: T[] ): Thenable { @@ -121,7 +123,11 @@ export function notify( } else if (options.telemetry instanceof Error) { Telemetry.sendException(options.telemetry); } else { - Telemetry.sendEvent(options.telemetry); + Telemetry.sendEvent( + options.telemetry, + options.properties, + options.measurements, + ); } } diff --git a/src/utils/workspace.ts b/src/utils/workspace.ts index a7bc2dfa..16377d98 100644 --- a/src/utils/workspace.ts +++ b/src/utils/workspace.ts @@ -22,7 +22,9 @@ import { WorkspaceEdit, } from "vscode"; -import { Telemetry } from "./telemetryClient"; +import { MessageKind, notify } from "./notifications"; + +const logger = "workspace"; export function getWorkspaceRoot( ignoreException: boolean = false, @@ -93,7 +95,11 @@ export async function addWorkspaceFile( const telemetryStats = await getWorkbookStatistics(ext, directory); const isPython = ext === ".kdb.py" ? ".Python" : ".q"; - Telemetry.sendEvent("Workbook.Create" + isPython, {}, telemetryStats); + notify("Workbook created.", MessageKind.DEBUG, { + logger, + telemetry: "Workbook.Create" + isPython, + measurements: telemetryStats, + }); return uri; } From cf2222676a38b471acc3b7106ea785b4b9550d41 Mon Sep 17 00:00:00 2001 From: ecmel Date: Thu, 26 Jun 2025 22:29:40 +0300 Subject: [PATCH 08/10] make progress notifications consistent --- src/classes/insightsConnection.ts | 283 ++++++++++------------- src/commands/buildToolsCommand.ts | 3 +- src/commands/dataSourceCommand.ts | 4 +- src/commands/installTools.ts | 2 +- src/commands/serverCommand.ts | 77 +++--- src/commands/workspaceCommand.ts | 33 ++- src/extension.ts | 16 +- src/services/connectionManagerService.ts | 1 - src/services/dataSourceEditorProvider.ts | 41 +++- src/services/notebookController.ts | 7 +- src/utils/core.ts | 43 ++-- 11 files changed, 238 insertions(+), 272 deletions(-) diff --git a/src/classes/insightsConnection.ts b/src/classes/insightsConnection.ts index 23a32217..2fac90be 100644 --- a/src/classes/insightsConnection.ts +++ b/src/classes/insightsConnection.ts @@ -41,7 +41,7 @@ import { tokenUndefinedError, } from "../utils/core"; import { convertTimeToTimestamp } from "../utils/dataSource"; -import { MessageKind, Runner, notify } from "../utils/notifications"; +import { MessageKind, notify, Runner } from "../utils/notifications"; import { generateQSqlBody, handleScratchpadTableRes, @@ -85,7 +85,10 @@ export class InsightsConnection { await this.getConfig(); await this.getMeta(); await this.getApiConfig(); - this.getScratchpadQuery("", undefined, false, true, false); + + const runner = Runner.create(() => this.getScratchpadQuery("")); + runner.title = `Starting scratchpad on ${this.connLabel}.`; + runner.execute(); } }); return this.connected; @@ -383,46 +386,35 @@ export class InsightsConnection { return undefined; } options.responseType = "arraybuffer"; - const runner = Runner.create(async (progress, token) => { - token.onCancellationRequested(() => { - notify(`User cancelled the Datasource Run.`, MessageKind.DEBUG, { - logger, - }); - }); - progress.report({ message: "Query executing..." }); - - return await axios(options) - .then((response: any) => { - notify( - `[Datasource RUN] Status: ${response.status}.`, - MessageKind.DEBUG, - { logger }, - ); - if (isCompressed(response.data)) { - response.data = uncompress(response.data); - } - return { - error: "", - arrayBuffer: response.data.buffer - ? response.data.buffer - : response.data, - }; - }) - .catch((error: any) => { - notify( - `[Datasource RUN] Status: ${error.response.status}.`, - MessageKind.ERROR, - { logger, params: error }, - ); - return { - error: { buffer: error.response.data }, - arrayBuffer: undefined, - }; - }); - }); - - return await runner.execute(); + return await axios(options) + .then((response: any) => { + notify( + `[Datasource RUN] Status: ${response.status}.`, + MessageKind.DEBUG, + { logger }, + ); + if (isCompressed(response.data)) { + response.data = uncompress(response.data); + } + return { + error: "", + arrayBuffer: response.data.buffer + ? response.data.buffer + : response.data, + }; + }) + .catch((error: any) => { + notify( + `[Datasource RUN] Status: ${error.response.status}.`, + MessageKind.ERROR, + { logger, params: error }, + ); + return { + error: { buffer: error.response.data }, + arrayBuffer: undefined, + }; + }); } } @@ -506,44 +498,30 @@ export class InsightsConnection { return; } - const runner = Runner.create(async (progress, token) => { - token.onCancellationRequested(() => { - notify(`User cancelled the scratchpad import.`, MessageKind.DEBUG, { - logger, - }); - }); - - progress.report({ message: "Populating scratchpad..." }); - - return await axios(options).then((response: any) => { - if (response.data.error) { - notify( - "Error occured while populating scratchpad.", - MessageKind.ERROR, - { - logger, - params: response.data.errorMsg, - telemetry: - "Datasource." + - dsTypeString + - ".Scratchpad.Populated.Errored", - }, - ); - } else { - notify( - `Executed successfully, stored in ${variableName}.`, - MessageKind.INFO, - { - logger, - params: { status: response.status, params: body.params }, - telemetry: - "Datasource." + dsTypeString + ".Scratchpad.Populated", - }, - ); - } - }); + return await axios(options).then((response: any) => { + if (response.data.error) { + notify( + "Error occured while populating scratchpad.", + MessageKind.ERROR, + { + logger, + params: response.data.errorMsg, + telemetry: + "Datasource." + dsTypeString + ".Scratchpad.Populated.Errored", + }, + ); + } else { + notify( + `Executed successfully, stored in ${variableName}.`, + MessageKind.INFO, + { + logger, + params: { status: response.status, params: body.params }, + telemetry: "Datasource." + dsTypeString + ".Scratchpad.Populated", + }, + ); + } }); - await runner.execute(); } else { this.noConnectionOrEndpoints(); } @@ -601,28 +579,24 @@ export class InsightsConnection { return; } - const runner = Runner.create(async () => { - const udaRes = await axios(options).then((response: any) => { - if (response.data.error) { - return response.data; - } else { - notify(`Status: ${response.status}`, MessageKind.DEBUG, { - logger, - }); - if (!response.data.error) { - if (isTableView) { - response.data = JSON.parse( - response.data.data, - ) as StructuredTextResults; - } - return response.data; + return await axios(options).then((response: any) => { + if (response.data.error) { + return response.data; + } else { + notify(`Status: ${response.status}`, MessageKind.DEBUG, { + logger, + }); + if (!response.data.error) { + if (isTableView) { + response.data = JSON.parse( + response.data.data, + ) as StructuredTextResults; } return response.data; } - }); - return udaRes; + return response.data; + } }); - return await runner.execute(); } else { this.noConnectionOrEndpoints(); } @@ -633,7 +607,6 @@ export class InsightsConnection { query: string, context?: string, isPython?: boolean, - isStarting?: boolean, isTableView?: boolean, ): Promise { if (this.connected && this.connEndpoints) { @@ -676,64 +649,46 @@ export class InsightsConnection { return; } - const runner = Runner.create(async (progress, token) => { - token.onCancellationRequested(() => { + return await axios(options).then((response: any) => { + if (response.data.error) { + return response.data; + } else if (query === "") { notify( - `User cancelled the scratchpad execution.`, + `Scratchpad created for connection: ${this.connLabel}.`, MessageKind.DEBUG, { logger }, ); - }); - - if (isStarting) { - progress.report({ message: "Starting scratchpad..." }); } else { - progress.report({ message: "Query is running..." }); - } + notify(`Status: ${response.status}`, MessageKind.DEBUG, { + logger, + }); + if (!response.data.error) { + if (isTableView) { + if ( + /* TODO: Workaround for Python structuredText bug */ + !isPython && + this.insightsVersion && + isBaseVersionGreaterOrEqual(this.insightsVersion, 1.12) + ) { + response.data = JSON.parse( + response.data.data, + ) as StructuredTextResults; + } else { + const buffer = new Uint8Array( + response.data.data.map((x: string) => parseInt(x, 16)), + ).buffer; - const spRes = await axios(options).then((response: any) => { - if (response.data.error) { - return response.data; - } else if (query === "") { - notify( - `Scratchpad created for connection: ${this.connLabel}.`, - MessageKind.DEBUG, - { logger }, - ); - } else { - notify(`Status: ${response.status}`, MessageKind.DEBUG, { - logger, - }); - if (!response.data.error) { - if (isTableView) { - if ( - /* TODO: Workaround for Python structuredText bug */ - !isPython && - this.insightsVersion && - isBaseVersionGreaterOrEqual(this.insightsVersion, 1.12) - ) { - response.data = JSON.parse( - response.data.data, - ) as StructuredTextResults; - } else { - const buffer = new Uint8Array( - response.data.data.map((x: string) => parseInt(x, 16)), - ).buffer; - - response.data.data = handleWSResults(buffer, isTableView); - response.data.data = handleScratchpadTableRes( - response.data.data, - ); - } + response.data.data = handleWSResults(buffer, isTableView); + response.data.data = handleScratchpadTableRes( + response.data.data, + ); } - return response.data; } return response.data; } - }); - return spRes; + return response.data; + } }); - return await runner.execute(); } else { this.noConnectionOrEndpoints(); } @@ -758,29 +713,23 @@ export class InsightsConnection { return; } - const runner = Runner.create(async (progress) => { - progress.report({ message: "Reseting scratchpad..." }); - const res = await axios(options) - .then((_response: any) => { - notify( - `Executed successfully, scratchpad reset at ${this.connLabel} connection.`, - MessageKind.INFO, - { logger, telemetry: "Scratchpad.Reseted" }, - ); - return true; - }) - .catch((_error: any) => { - notify( - `Error occurred while resetting scratchpad in connection ${this.connLabel}, try again.`, - MessageKind.ERROR, - { logger }, - ); - return false; - }); - - return res; - }); - return await runner.execute(); + return await axios(options) + .then((_response: any) => { + notify( + `Executed successfully, scratchpad reset at ${this.connLabel} connection.`, + MessageKind.INFO, + { logger, telemetry: "Scratchpad.Reseted" }, + ); + return true; + }) + .catch((_error: any) => { + notify( + `Error occurred while resetting scratchpad in connection ${this.connLabel}, try again.`, + MessageKind.ERROR, + { logger }, + ); + return false; + }); } else { this.noConnectionOrEndpoints(); return false; diff --git a/src/commands/buildToolsCommand.ts b/src/commands/buildToolsCommand.ts index f3b4294a..df9a8b19 100644 --- a/src/commands/buildToolsCommand.ts +++ b/src/commands/buildToolsCommand.ts @@ -25,6 +25,7 @@ import { } from "vscode"; import { ext } from "../extensionVariables"; +import { getBasename } from "../utils/core"; import { Runner } from "../utils/notifications"; const cache = new Map>(); @@ -207,7 +208,7 @@ function lint(document: TextDocument) { throw new Error(`Linting Failed ${error}`); } }); - runner.title = "Linting"; + runner.title = `Linting ${getBasename(document.uri)}.`; return runner.execute(); } diff --git a/src/commands/dataSourceCommand.ts b/src/commands/dataSourceCommand.ts index 48e9608f..90f35fd0 100644 --- a/src/commands/dataSourceCommand.ts +++ b/src/commands/dataSourceCommand.ts @@ -33,7 +33,7 @@ import { scratchpadVariableInput } from "../models/items/server"; import { UDARequestBody } from "../models/uda"; import { DataSourcesPanel } from "../panels/datasource"; import { ConnectionManagementService } from "../services/connectionManagerService"; -import { noSelectedConnectionAction, offerConnectAction } from "../utils/core"; +import { noSelectedConnectionAction } from "../utils/core"; import { checkIfTimeParamIsCorrect, convertTimeToTimestamp, @@ -100,7 +100,6 @@ export async function populateScratchpad( selectedConnection instanceof LocalConnection || !selectedConnection ) { - offerConnectAction(connLabel); DataSourcesPanel.running = false; return; } @@ -144,7 +143,6 @@ export async function runDataSource( try { if (selectedConnection instanceof LocalConnection || !selectedConnection) { - offerConnectAction(connLabel); return; } selectedConnection.getMeta(); diff --git a/src/commands/installTools.ts b/src/commands/installTools.ts index 31a1c00f..105550c1 100644 --- a/src/commands/installTools.ts +++ b/src/commands/installTools.ts @@ -209,7 +209,7 @@ export async function installTools(): Promise { }); } }); - runner.title = "Installing q..."; + runner.title = "Installing q."; runner.execute().then(async () => { notify( onboardingWorkflow.prompt(ext.context.globalStorageUri.fsPath), diff --git a/src/commands/serverCommand.ts b/src/commands/serverCommand.ts index 452a409e..0e737d67 100644 --- a/src/commands/serverCommand.ts +++ b/src/commands/serverCommand.ts @@ -25,6 +25,7 @@ import { window, workspace, env, + ProgressLocation, } from "vscode"; import { ext } from "../extensionVariables"; @@ -834,42 +835,12 @@ export async function executeQuery( isPython: boolean, isWorkbook: boolean, isFromConnTree?: boolean, -): Promise { - const runner = Runner.create((_, token) => - _executeQuery( - query, - connLabel, - executorName, - context, - isPython, - isWorkbook, - isFromConnTree, - token, - ), - ); - runner.title = `Executing query (${executorName})`; - await runner.execute(); -} - -export async function _executeQuery( - query: string, - connLabel: string, - executorName: string, - context: string, - isPython: boolean, - isWorkbook: boolean, - isFromConnTree?: boolean, token?: CancellationToken, ): Promise { const connMngService = new ConnectionManagementService(); const queryConsole = ExecutionConsole.start(); if (connLabel === "") { if (ext.activeConnection === undefined) { - notify( - "You aren't connected to any connection. Once connected please try again.", - MessageKind.ERROR, - { logger }, - ); return undefined; } else { connLabel = ext.activeConnection.connLabel; @@ -877,7 +848,7 @@ export async function _executeQuery( } const isConnected = connMngService.isConnected(connLabel); if (!isConnected) { - notify("The selected connection is not connected.", MessageKind.ERROR, { + notify(`Connection ${connLabel} is not connected.`, MessageKind.ERROR, { logger, }); return undefined; @@ -1090,23 +1061,35 @@ export function runQuery( break; } } - if (target) { - runDataSource( - { - dataSource: { - selectedType: "QSQL", - qsql: { - query, - selectedTarget: target, + + const runner = Runner.create(() => { + return target + ? runDataSource( + { + dataSource: { + selectedType: "QSQL", + qsql: { + query, + selectedTarget: target, + }, + }, }, - }, - }, - connLabel, - executorName, - ); - } else { - executeQuery(query, connLabel, executorName, context, isPython, isWorkbook); - } + connLabel, + executorName, + ) + : executeQuery( + query, + connLabel, + executorName, + context, + isPython, + isWorkbook, + ); + }); + + runner.location = ProgressLocation.Notification; + runner.title = `Executing ${executorName} on ${connLabel || "active connection"}.`; + runner.execute(); } export function rerunQuery(rerunQueryElement: QueryHistory) { diff --git a/src/commands/workspaceCommand.ts b/src/commands/workspaceCommand.ts index db29f684..4089246e 100644 --- a/src/commands/workspaceCommand.ts +++ b/src/commands/workspaceCommand.ts @@ -32,7 +32,7 @@ import { ExecutionTypes } from "../models/execution"; import { MetaDap } from "../models/meta"; import { ConnectionManagementService } from "../services/connectionManagerService"; import { InsightsNode, KdbNode, LabelNode } from "../services/kdbTreeProvider"; -import { offerConnectAction } from "../utils/core"; +import { getBasename, offerConnectAction } from "../utils/core"; import { importOldDsFiles, oldFilesExists } from "../utils/dataSource"; import { MessageKind, notify, Runner } from "../utils/notifications"; import { normalizeAssemblyTarget } from "../utils/shared"; @@ -197,14 +197,11 @@ export async function pickTarget(uri: Uri) { let daps: MetaDap[] = []; - if (!isPython(uri)) { - const connMngService = new ConnectionManagementService(); - const connected = connMngService.isConnected(conn.label); - if (connected) { - daps = JSON.parse(connMngService.retrieveMetaContent(conn.label, "DAP")); - } else { - offerConnectAction(server); - } + const connMngService = new ConnectionManagementService(); + const connected = connMngService.isConnected(conn.label); + + if (!isPython(uri) && connected) { + daps = JSON.parse(connMngService.retrieveMetaContent(conn.label, "DAP")); } const target = getTargetForUri(uri); @@ -212,9 +209,9 @@ export async function pickTarget(uri: Uri) { const exists = daps.some( (value) => `${value.assembly} ${value.instance}` === target, ); - if (!exists) { + if (!exists && !connected) { const [assembly, instance] = target.split(/\s+/); - daps.push({ assembly, instance } as MetaDap); + daps.unshift({ assembly, instance } as MetaDap); } } @@ -224,7 +221,7 @@ export async function pickTarget(uri: Uri) { ...daps.map((value) => `${value.assembly} ${value.instance}`), ], { - title: `Choose a target on ${server}`, + title: `Choose a target on ${server} (${connected ? "Connected" : "Disconnected"})`, placeHolder: target, }, ); @@ -276,12 +273,13 @@ export async function runActiveEditor(type?: ExecutionTypes) { } else { throw new Error("Connection for not found"); } + } else if (ext.activeConnection === undefined) { + offerConnectAction(); + return; } - const executorName = - ext.activeTextEditor.document.fileName.split("/").pop() || ""; - const target = isInsights ? getTargetForUri(uri) : undefined; + const executorName = getBasename(ext.activeTextEditor.document.uri); runQuery( type === undefined @@ -409,7 +407,7 @@ export async function importOldDSFiles() { notify("No workspace folder found.", MessageKind.ERROR, { logger }); return; } - const runner = Runner.create(async (progress, token) => { + const runner = Runner.create(async (_, token) => { token.onCancellationRequested(() => { notify("User cancelled the old DS files import.", MessageKind.DEBUG, { logger, @@ -417,10 +415,9 @@ export async function importOldDSFiles() { return false; }); - progress.report({ message: "Importing old DS files..." }); await importOldDsFiles(); - return; }); + runner.title = "Importing old DS files."; return await runner.execute(); } else { notify("No old Datasource files found on your VSCODE.", MessageKind.INFO, { diff --git a/src/extension.ts b/src/extension.ts index 858146d1..a0cc434c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -117,7 +117,7 @@ import { import { runQFileTerminal } from "./utils/execution"; import { handleFeedbackSurvey } from "./utils/feedbackSurveyUtils"; import { getIconPath } from "./utils/iconsUtils"; -import { MessageKind, notify } from "./utils/notifications"; +import { MessageKind, notify, Runner } from "./utils/notifications"; import AuthSettings from "./utils/secretStorage"; import { Telemetry } from "./utils/telemetryClient"; import { @@ -784,14 +784,22 @@ function registerConnectionsCommands(): CommandRegistration[] { { command: "kdb.connections.refresh.serverObjects", callback: async () => { - ext.serverProvider.reload(); - await refreshGetMeta(); + const runner = Runner.create(() => { + ext.serverProvider.reload(); + return refreshGetMeta(); + }); + runner.location = vscode.ProgressLocation.Notification; + runner.title = "Refreshing server objects for all connections."; + await runner.execute(); }, }, { command: "kdb.connections.refresh.meta", callback: async (viewItem: InsightsNode) => { - await refreshGetMeta(viewItem.label); + const runner = Runner.create(() => refreshGetMeta(viewItem.label)); + runner.location = vscode.ProgressLocation.Notification; + runner.title = `Refreshing meta data for ${viewItem.label || "all connections"}.`; + await runner.execute(); }, }, { diff --git a/src/services/connectionManagerService.ts b/src/services/connectionManagerService.ts index 2ff283c1..a625dea1 100644 --- a/src/services/connectionManagerService.ts +++ b/src/services/connectionManagerService.ts @@ -366,7 +366,6 @@ export class ConnectionManagementService { command, context, isPython, - false, !stringify, ); } diff --git a/src/services/dataSourceEditorProvider.ts b/src/services/dataSourceEditorProvider.ts index eba4b900..b4bcc89f 100644 --- a/src/services/dataSourceEditorProvider.ts +++ b/src/services/dataSourceEditorProvider.ts @@ -17,6 +17,7 @@ import { CustomTextEditorProvider, Disposable, ExtensionContext, + ProgressLocation, Range, TextDocument, Webview, @@ -42,7 +43,7 @@ import { import { DataSourceCommand, DataSourceMessage2 } from "../models/messages"; import { MetaObjectPayload } from "../models/meta"; import { UDA } from "../models/uda"; -import { offerConnectAction } from "../utils/core"; +import { getBasename, offerConnectAction } from "../utils/core"; import { getNonce } from "../utils/getNonce"; import { getUri } from "../utils/getUri"; import { MessageKind, Runner, notify } from "../utils/notifications"; @@ -164,6 +165,9 @@ export class DataSourceEditorProvider implements CustomTextEditorProvider { /* istanbul ignore next */ webview.onDidReceiveMessage(async (msg: DataSourceMessage2) => { + const selectedServer = getServerForUri(document.uri) || ""; + const connected = connMngService.isConnected(selectedServer); + switch (msg.command) { case DataSourceCommand.Server: { await setServerForUri(document.uri, msg.selectedServer); @@ -191,14 +195,14 @@ export class DataSourceEditorProvider implements CustomTextEditorProvider { break; } case DataSourceCommand.Refresh: { - const selectedServer = getServerForUri(document.uri) || ""; - if (connMngService.isConnected(selectedServer)) { + if (connected) { const runner = Runner.create(async () => { await connMngService.refreshGetMeta(selectedServer); this.cache.delete(selectedServer); updateWebview(); }); - runner.title = "Refreshing meta data"; + runner.location = ProgressLocation.Notification; + runner.title = `Refreshing meta data for ${selectedServer}.`; await runner.execute(); } else { offerConnectAction(selectedServer); @@ -206,15 +210,32 @@ export class DataSourceEditorProvider implements CustomTextEditorProvider { break; } case DataSourceCommand.Run: { - await runDataSource( - msg.dataSourceFile, - msg.selectedServer, - this.filenname, - ); + if (connected) { + const runner = Runner.create(() => + runDataSource( + msg.dataSourceFile, + msg.selectedServer, + this.filenname, + ), + ); + runner.location = ProgressLocation.Notification; + runner.title = `Running ${getBasename(document.uri)} on ${msg.selectedServer}.`; + await runner.execute(); + } else { + offerConnectAction(selectedServer); + } break; } case DataSourceCommand.Populate: { - await populateScratchpad(msg.dataSourceFile, msg.selectedServer); + if (connected) { + const runner = Runner.create(() => + populateScratchpad(msg.dataSourceFile, msg.selectedServer), + ); + runner.title = "Populating scratchpad."; + await runner.execute(); + } else { + offerConnectAction(selectedServer); + } break; } } diff --git a/src/services/notebookController.ts b/src/services/notebookController.ts index 68abc49f..09044ad6 100644 --- a/src/services/notebookController.ts +++ b/src/services/notebookController.ts @@ -16,6 +16,7 @@ import * as vscode from "vscode"; import { InsightsConnection } from "../classes/insightsConnection"; import { ext } from "../extensionVariables"; import { ConnectionManagementService } from "../services/connectionManagerService"; +import { offerConnectAction } from "../utils/core"; import { MessageKind, notify, timeout } from "../utils/notifications"; import { resultToBase64 } from "../utils/queryUtils"; import { convertToGrid, formatResult } from "../utils/resultsRenderer"; @@ -57,11 +58,7 @@ export class KxNotebookController { ): Promise { const conn = ext.activeConnection; if (conn === undefined) { - notify( - "You aren't connected to any connection. Once connected please try again.", - MessageKind.ERROR, - { logger }, - ); + offerConnectAction(); return; } const manager = this.createConnectionManager(); diff --git a/src/utils/core.ts b/src/utils/core.ts index a3ed24f5..8006bf7b 100644 --- a/src/utils/core.ts +++ b/src/utils/core.ts @@ -15,6 +15,7 @@ import { ChildProcess } from "child_process"; import { createHash } from "crypto"; import { writeFile } from "fs/promises"; import { pathExists } from "fs-extra"; +import path from "node:path"; import { env } from "node:process"; import { tmpdir } from "os"; import { join } from "path"; @@ -343,21 +344,29 @@ export function invalidUsernameJWT(connLabel: string): void { } /* istanbul ignore next */ -export function offerConnectAction(connLabel: string): void { - notify( - `You aren't connected to ${connLabel}, would you like to connect? Once connected please try again.`, - MessageKind.INFO, - {}, - "Connect", - "Cancel", - ).then(async (result) => { - if (result === "Connect") { - await commands.executeCommand( - "kdb.connections.connect.via.dialog", - connLabel, - ); - } - }); +export function offerConnectAction(connLabel?: string): void { + if (connLabel) { + notify( + `You aren't connected to ${connLabel}, would you like to connect? Once connected please try again.`, + MessageKind.WARNING, + {}, + "Connect", + "Cancel", + ).then(async (result) => { + if (result === "Connect") { + await commands.executeCommand( + "kdb.connections.connect.via.dialog", + connLabel, + ); + } + }); + } else { + notify( + "You aren't connected to any connection. Once connected please try again.", + MessageKind.WARNING, + { logger }, + ); + } } export function noSelectedConnectionAction(): void { @@ -701,3 +710,7 @@ export function isBaseVersionGreaterOrEqual( ): boolean { return semver.gte(`${baseVersion}.0`, `${targetVersion}.0`); } + +export function getBasename(uri: Uri): string { + return path.basename(uri.path); +} From d98935566cd4b5f337bdba5bf598db4a2254c81a Mon Sep 17 00:00:00 2001 From: ecmel Date: Fri, 27 Jun 2025 08:15:15 +0300 Subject: [PATCH 09/10] fixed notebook errors on a IE connection are not as descriptive as kdb --- src/classes/insightsConnection.ts | 4 +- src/commands/serverCommand.ts | 22 ++++++-- src/services/notebookController.ts | 23 ++++---- src/utils/notifications.ts | 10 ++-- test/suite/notebooks.test.ts | 85 +++++++++++------------------- 5 files changed, 63 insertions(+), 81 deletions(-) diff --git a/src/classes/insightsConnection.ts b/src/classes/insightsConnection.ts index 2fac90be..e05739f4 100644 --- a/src/classes/insightsConnection.ts +++ b/src/classes/insightsConnection.ts @@ -716,7 +716,7 @@ export class InsightsConnection { return await axios(options) .then((_response: any) => { notify( - `Executed successfully, scratchpad reset at ${this.connLabel} connection.`, + `Scratchpad reset for ${this.connLabel} executed successfully.`, MessageKind.INFO, { logger, telemetry: "Scratchpad.Reseted" }, ); @@ -724,7 +724,7 @@ export class InsightsConnection { }) .catch((_error: any) => { notify( - `Error occurred while resetting scratchpad in connection ${this.connLabel}, try again.`, + `Error occurred while resetting scratchpad for ${this.connLabel}, try again.`, MessageKind.ERROR, { logger }, ); diff --git a/src/commands/serverCommand.ts b/src/commands/serverCommand.ts index 0e737d67..8d5be098 100644 --- a/src/commands/serverCommand.ts +++ b/src/commands/serverCommand.ts @@ -836,7 +836,7 @@ export async function executeQuery( isWorkbook: boolean, isFromConnTree?: boolean, token?: CancellationToken, -): Promise { +): Promise { const connMngService = new ConnectionManagementService(); const queryConsole = ExecutionConsole.start(); if (connLabel === "") { @@ -883,7 +883,8 @@ export async function executeQuery( ); return undefined; } - const isStringfy = !ext.isResultsTabVisible; + const isNotebook = executorName.endsWith(".kxnb"); + const isStringfy = isNotebook ? false : !ext.isResultsTabVisible; const startTime = Date.now(); const results = await connMngService.executeQuery( query, @@ -902,7 +903,7 @@ export async function executeQuery( // set context for root nodes if (selectedConn instanceof InsightsConnection) { - await writeScratchpadResult( + const res = await writeScratchpadResult( results, query, connLabel, @@ -912,6 +913,11 @@ export async function executeQuery( duration, connVersion, ); + if (isNotebook) { + return res; + } + } else if (isNotebook) { + return results; } else { /* istanbul ignore next */ if (ext.isResultsTabVisible) { @@ -1062,7 +1068,7 @@ export function runQuery( } } - const runner = Runner.create(() => { + const runner = Runner.create((_, token) => { return target ? runDataSource( { @@ -1084,6 +1090,8 @@ export function runQuery( context, isPython, isWorkbook, + false, + token, ); }); @@ -1336,7 +1344,7 @@ export async function writeScratchpadResult( isWorkbook: boolean, duration: string, connVersion: number, -): Promise { +): Promise { const telemetryLangType = isPython ? ".Python" : ".q"; const telemetryBaseMsg = isWorkbook ? "Workbook" : "Scratchpad"; let errorMsg; @@ -1359,6 +1367,10 @@ export async function writeScratchpadResult( } } + if (executorName.endsWith(".kxnb")) { + return errorMsg ?? result; + } + if (ext.isResultsTabVisible) { await writeQueryResultsToView( errorMsg ?? result, diff --git a/src/services/notebookController.ts b/src/services/notebookController.ts index 09044ad6..5329ada0 100644 --- a/src/services/notebookController.ts +++ b/src/services/notebookController.ts @@ -14,10 +14,10 @@ import * as vscode from "vscode"; import { InsightsConnection } from "../classes/insightsConnection"; +import { executeQuery } from "../commands/serverCommand"; import { ext } from "../extensionVariables"; -import { ConnectionManagementService } from "../services/connectionManagerService"; -import { offerConnectAction } from "../utils/core"; -import { MessageKind, notify, timeout } from "../utils/notifications"; +import { getBasename, offerConnectAction } from "../utils/core"; +import { MessageKind, notify } from "../utils/notifications"; import { resultToBase64 } from "../utils/queryUtils"; import { convertToGrid, formatResult } from "../utils/resultsRenderer"; @@ -47,13 +47,9 @@ export class KxNotebookController { this.controller.dispose(); } - createConnectionManager() { - return new ConnectionManagementService(); - } - async execute( cells: vscode.NotebookCell[], - _notebook: vscode.NotebookDocument, + notebook: vscode.NotebookDocument, _controller: vscode.NotebookController, ): Promise { const conn = ext.activeConnection; @@ -61,7 +57,6 @@ export class KxNotebookController { offerConnectAction(); return; } - const manager = this.createConnectionManager(); const isInsights = conn instanceof InsightsConnection; const connVersion = isInsights ? (conn.insightsVersion ?? 0) : 0; @@ -73,12 +68,15 @@ export class KxNotebookController { try { const results = await Promise.race([ - manager.executeQuery( + executeQuery( cell.document.getText(), conn.connLabel, + getBasename(notebook.uri), ".", - false, isPython, + false, + false, + execution.token, ), new Promise((_, reject) => { const updateCancelled = () => { @@ -89,7 +87,6 @@ export class KxNotebookController { updateCancelled(); execution.token.onCancellationRequested(updateCancelled); }), - timeout(), ]); const rendered = render(results, isPython, isInsights, connVersion); @@ -100,7 +97,7 @@ export class KxNotebookController { ]), ]); } catch (error) { - notify("Unable to run code block.", MessageKind.ERROR, { + notify("Notebook execution stopped.", MessageKind.DEBUG, { logger, params: error, }); diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index fdb2760f..6db27c01 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -68,7 +68,6 @@ export class Runner { token.onCancellationRequested(updateCancelled); updateCancelled(); }), - timeout(), ]) : this.executor(progress, token); }, @@ -84,12 +83,6 @@ export function sleep(ms: number): Promise { return new Promise((resolve, _) => setTimeout(() => resolve(), ms)); } -export function timeout(ms = 1000 * 60 * 5): Promise { - return new Promise((_, reject) => - setTimeout(() => reject(new vscode.CancellationError()), ms), - ); -} - export const enum MessageKind { DEBUG = "DEBUG", INFO = "INFO", @@ -161,6 +154,9 @@ export function notify( function getParams(params?: any) { if (params) { try { + if (params instanceof Error) { + return JSON.stringify({ message: params.message }); + } return JSON.stringify(params); } catch (error) { return `Parsing log params failed: ${error}`; diff --git a/test/suite/notebooks.test.ts b/test/suite/notebooks.test.ts index 7c077e9f..72ca7ed7 100644 --- a/test/suite/notebooks.test.ts +++ b/test/suite/notebooks.test.ts @@ -15,12 +15,24 @@ import * as assert from "assert"; import * as sinon from "sinon"; import * as vscode from "vscode"; +import * as serverCommand from "../../src/commands/serverCommand"; import { ext } from "../../src/extensionVariables"; -import { ConnectionManagementService } from "../../src/services/connectionManagerService"; import * as controlller from "../../src/services/notebookController"; import * as serializer from "../../src/services/notebookSerializer"; describe("Notebooks", () => { + const notebook = { + uri: vscode.Uri.file("/test.kxnb"), + }; + + let activeConnectionStub: sinon.SinonStub; + let executeQueryStub: sinon.SinonStub; + + beforeEach(() => { + activeConnectionStub = sinon.stub(ext, "activeConnection"); + executeQueryStub = sinon.stub(serverCommand, "executeQuery"); + }); + afterEach(() => { sinon.restore(); }); @@ -51,17 +63,6 @@ describe("Notebooks", () => { let instance: controlller.KxNotebookController; - function createResult(result: any) { - sinon.stub(ext, "activeConnection").value({ connLabel: "test" }); - sinon.stub(instance, "createConnectionManager").returns(< - ConnectionManagementService - >({ - executeQuery() { - return result; - }, - })); - } - beforeEach(() => { sinon.stub(vscode.notebooks, "createNotebookController").returns(< vscode.NotebookController @@ -79,45 +80,31 @@ describe("Notebooks", () => { instance = undefined; }); - it("should show create connection manager", async () => { - const manager = instance.createConnectionManager(); - assert.ok(manager); - }); - it("should end execution on error", async () => { - createResult({}); + activeConnectionStub.value({}); + executeQueryStub.resolves({}); const end = sinon.stub(executor, "end"); - await instance.execute( - cells, - {}, - {}, - ); + await instance.execute(cells, notebook, {}); assert.ok(end.calledOnce); }); - it("should show error message if not connected", async () => { - const msg = sinon.stub(vscode.window, "showErrorMessage"); - await instance.execute( - cells, - {}, - {}, - ); + it("should show warning message if not connected", async () => { + const msg = sinon.stub(vscode.window, "showWarningMessage"); + await instance.execute(cells, notebook, {}); assert.ok(msg.calledOnce); }); it("should execute code for number result", async () => { - createResult(1); + activeConnectionStub.value({}); + executeQueryStub.resolves(1); const res = sinon.stub(executor, "replaceOutput"); - await instance.execute( - cells, - {}, - {}, - ); + await instance.execute(cells, notebook, {}); assert.ok(res.calledOnce); }); it("should execute code for plot result", async () => { - createResult({ + activeConnectionStub.value({}); + executeQueryStub.resolves({ count: 50046, columns: [ { @@ -138,36 +125,26 @@ describe("Notebooks", () => { ], }); const res = sinon.stub(executor, "replaceOutput"); - await instance.execute( - cells, - {}, - {}, - ); + await instance.execute(cells, notebook, {}); assert.ok(res.calledOnce); }); it("should execute code for string result", async () => { - createResult("result"); + activeConnectionStub.value({}); + executeQueryStub.resolves("result"); const res = sinon.stub(executor, "replaceOutput"); - await instance.execute( - cells, - {}, - {}, - ); + await instance.execute(cells, notebook, {}); assert.ok(res.calledOnce); }); it("should execute code for table result", async () => { - createResult({ + activeConnectionStub.value({}); + executeQueryStub.resolves({ count: 1, columns: [{ name: "values", type: "long", values: ["1"], order: [0] }], }); const res = sinon.stub(executor, "replaceOutput"); - await instance.execute( - cells, - {}, - {}, - ); + await instance.execute(cells, notebook, {}); assert.ok(res.calledOnce); }); }); From f4a8385ad380be744d3fa70f1845494c85c524b1 Mon Sep 17 00:00:00 2001 From: ecmel Date: Fri, 27 Jun 2025 10:05:13 +0300 Subject: [PATCH 10/10] fixed error serialization --- src/utils/notifications.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index 6db27c01..cee63b0f 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -155,7 +155,7 @@ function getParams(params?: any) { if (params) { try { if (params instanceof Error) { - return JSON.stringify({ message: params.message }); + return JSON.stringify({ name: params.name, message: params.message }); } return JSON.stringify(params); } catch (error) {