Skip to content

Commit aa0810a

Browse files
authored
Merge pull request #734 from KxSystems/ee-release
Release 1.17.1
2 parents 8cc9151 + 252c465 commit aa0810a

File tree

10 files changed

+702
-331
lines changed

10 files changed

+702
-331
lines changed

package-lock.json

Lines changed: 546 additions & 279 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"displayName": "kdb",
44
"description": "IDE support for kdb product suite including the q programming language",
55
"publisher": "KX",
6-
"version": "1.17.0",
6+
"version": "1.17.1",
77
"icon": "resources/images/kx-logo-vs.png",
88
"repository": {
99
"type": "git",
@@ -1218,7 +1218,7 @@
12181218
"esbuild-base": "rimraf out && node ./esbuild.mjs",
12191219
"watch": "npm run -S esbuild-base -- --sourcemap --watch",
12201220
"build": "npm run -S esbuild-base -- --sourcemap",
1221-
"vscode:prepublish": "npm run -S esbuild-base -- --minify --keep-names",
1221+
"vscode:prepublish": "npm run -S esbuild-base -- --minify",
12221222
"package": "vsce package",
12231223
"publish": "vsce publish",
12241224
"pretest": "rimraf out-test && tsc --outdir out-test --strict false",
@@ -1243,11 +1243,11 @@
12431243
"@types/sinon": "21.0.0",
12441244
"@types/vscode": "1.101.0",
12451245
"@types/vscode-webview": "1.57.5",
1246-
"@vscode/extension-telemetry": "1.0.0",
1246+
"@vscode/extension-telemetry": "^1.4.0",
12471247
"@vscode/python-extension": "1.0.6",
12481248
"@vscode/test-electron": "2.5.2",
12491249
"@vscode/vsce": "3.7.1",
1250-
"axios": "1.13.2",
1250+
"axios": "1.13.4",
12511251
"c8": "10.1.3",
12521252
"chevrotain": "10.5.0",
12531253
"cross-env": "10.1.0",
@@ -1260,7 +1260,7 @@
12601260
"extract-zip": "2.0.1",
12611261
"fs-extra": "11.3.3",
12621262
"glob": "13.0.0",
1263-
"jsdom": "27.2.0",
1263+
"jsdom": "27.4.0",
12641264
"jwt-decode": "4.0.0",
12651265
"kill-sync": "1.0.3",
12661266
"lit": "3.3.2",
@@ -1272,7 +1272,7 @@
12721272
"moment-duration-format": "2.3.2",
12731273
"moment-timezone": "0.6.0",
12741274
"node-q": "2.7.0",
1275-
"npm-check-updates": "19.3.1",
1275+
"npm-check-updates": "19.3.2",
12761276
"pick-port": "2.2.0",
12771277
"prettier": "3.6.2",
12781278
"rimraf": "6.1.2",
@@ -1285,5 +1285,10 @@
12851285
"vscode-languageserver": "9.0.1",
12861286
"vscode-languageserver-textdocument": "1.0.12",
12871287
"vscode-uri": "3.1.0"
1288+
},
1289+
"overrides": {
1290+
"chevrotain": {
1291+
"lodash": ">=4.17.23"
1292+
}
12881293
}
12891294
}

src/extension.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ import { getIconPath } from "./utils/iconsUtils";
116116
import { MessageKind, notify, Runner } from "./utils/notifications";
117117
import { showRegistrationNotification } from "./utils/registration";
118118
import AuthSettings from "./utils/secretStorage";
119-
import { Telemetry } from "./utils/telemetryClient";
119+
import { ExtensionTelemetry } from "./utils/telemetryClient";
120120
import { addWorkspaceFile, openWith, setUriContent } from "./utils/workspace";
121121

122122
const logger = "extension";
@@ -128,6 +128,18 @@ export async function activate(context: vscode.ExtensionContext) {
128128
ext.outputChannel = vscode.window.createOutputChannel("kdb", { log: true });
129129
ext.openSslVersion = await checkOpenSslInstalled();
130130

131+
const extensionPackage = context.extension.packageJSON;
132+
const extensionVersion = extensionPackage.version || "unknown";
133+
const aiConnString = extensionPackage.aiConnString;
134+
135+
const isRCExtension = extensionVersion.includes("rc");
136+
const enableTelemetry = isRCExtension
137+
? false
138+
: vscode.workspace.getConfiguration("telemetry").get("telemetryLevel") !==
139+
"off";
140+
141+
ext.telemetry = new ExtensionTelemetry(aiConnString, enableTelemetry);
142+
131143
getWorkspaceLabelsConnMap();
132144
getWorkspaceLabels();
133145

@@ -1037,7 +1049,7 @@ function registerAllExtensionCommands(): void {
10371049
}
10381050

10391051
export async function deactivate(): Promise<void> {
1040-
await Telemetry.dispose();
1052+
await ext.telemetry.dispose();
10411053

10421054
if (ext.client) {
10431055
return ext.client.stop();

src/extensionVariables.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
import {
1515
ExtensionContext,
16-
extensions,
1716
languages,
1817
LogOutputChannel,
1918
OutputChannel,
@@ -41,12 +40,10 @@ import { QueryHistoryProvider } from "./services/queryHistoryProvider";
4140
import { KdbResultsViewProvider } from "./services/resultsPanelProvider";
4241
import { WorkspaceTreeProvider } from "./services/workspaceTreeProvider";
4342
import AuthSettings from "./utils/secretStorage";
43+
import { ExtensionTelemetry } from "./utils/telemetryClient";
4444

4545
// eslint-disable-next-line @typescript-eslint/no-namespace
4646
export namespace ext {
47-
export const EXTENSION_VERSION =
48-
extensions.getExtension("KX.kdb")?.packageJSON.version || "unknown";
49-
export const isRCExtension = EXTENSION_VERSION.includes("rc");
5047
export const REPL = "REPL";
5148
export let activeTextEditor: TextEditor | undefined;
5249
export let context: ExtensionContext;
@@ -68,6 +65,7 @@ export namespace ext {
6865
export let openSslVersion: string | null;
6966
export let resultPanelCSV: string;
7067
export let isDatasourceExecution: boolean;
68+
export let telemetry: ExtensionTelemetry;
7169
export const rowLimit = 150000000;
7270

7371
export let activeConnection: LocalConnection | InsightsConnection | undefined;
@@ -136,10 +134,6 @@ export namespace ext {
136134

137135
export let client: LanguageClient;
138136

139-
const extensionId = "kx.kdb";
140-
const packageJSON = extensions.getExtension(extensionId)!.packageJSON;
141-
export const extAIConnString = packageJSON.aiConnString;
142-
143137
export const localhost = "127.0.0.1";
144138
export const networkProtocols = {
145139
http: "http://",

src/utils/core.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,16 @@ function loadEnvironment(folder: string, env: { [key: string]: string }) {
140140
const [key, value] = trimmed.split("=");
141141
if (key && value !== undefined) {
142142
env[key.trim()] = value
143+
// Normalize
143144
.replace(/["']/gs, "")
144-
.replace(/(?:\$HOME|~)/gs, homedir())
145+
// Variable expansion
146+
.replace(
147+
/\$([A-Za-z_]+[A-Za-z0-9_]*)|\${([A-Za-z0-9_]*)}|%([A-Za-z_]+[A-Za-z0-9_]*)%/g,
148+
(match, a, b, c) => {
149+
const v = a ?? b ?? c;
150+
return env[v] ?? process.env[v] ?? match;
151+
},
152+
)
145153
.trim();
146154
}
147155
}

src/utils/notifications.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import * as vscode from "vscode";
1616
import { ext } from "../extensionVariables";
1717
import { kdbOutputLog } from "./loggers";
1818
import { stripUnprintableChars } from "./shared";
19-
import { Telemetry } from "./telemetryClient";
2019

2120
const logger = "notifications";
2221

@@ -132,11 +131,11 @@ export function notify<T extends string>(
132131

133132
if (options.telemetry) {
134133
if (typeof options.telemetry === "boolean") {
135-
Telemetry.sendError(new Error(message));
134+
ext.telemetry.sendError(new Error(message));
136135
} else if (options.telemetry instanceof Error) {
137-
Telemetry.sendError(options.telemetry);
136+
ext.telemetry.sendError(options.telemetry);
138137
} else {
139-
Telemetry.sendEvent(
138+
ext.telemetry.sendEvent(
140139
options.telemetry,
141140
options.properties,
142141
options.measurements,

src/utils/telemetryClient.ts

Lines changed: 5 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -12,35 +12,23 @@
1212
*/
1313

1414
import { TelemetryReporter } from "@vscode/extension-telemetry";
15-
import * as crypto from "crypto";
16-
import * as os from "os";
17-
import { OutputChannel, window, workspace } from "vscode";
15+
import { OutputChannel, window } from "vscode";
1816

19-
import { ext } from "../extensionVariables";
20-
21-
class ExtensionTelemetry {
17+
export class ExtensionTelemetry {
2218
private readonly output?: OutputChannel;
2319
private readonly reporter?: TelemetryReporter;
2420

2521
private readonly defaultProperties: { [key: string]: any } = {};
2622

27-
constructor() {
28-
const isEnableTelemetry = ext.isRCExtension
29-
? false
30-
: (workspace.getConfiguration("telemetry").get("enableTelemetry") ??
31-
true);
23+
constructor(aiConnString: string, enableTelemetry = true) {
3224
const isTestRun = process.env.CODE_TEST || false;
3325

34-
if (isEnableTelemetry) {
26+
if (enableTelemetry) {
3527
if (isTestRun) {
3628
this.output = window.createOutputChannel("telemetry-client-test");
3729
} else {
3830
try {
39-
this.reporter = new TelemetryReporter(ext.extAIConnString);
40-
this.defaultProperties["common.vscodemachineid"] =
41-
generateMachineId();
42-
this.defaultProperties["common.vscodesessionid"] =
43-
generateSessionId();
31+
this.reporter = new TelemetryReporter(aiConnString);
4432
} catch (error) {
4533
console.log(error);
4634
}
@@ -88,10 +76,6 @@ class ExtensionTelemetry {
8876
}
8977
}
9078

91-
public obfuscate(data: string): string {
92-
return crypto.createHash("sha256").update(data).digest("base64");
93-
}
94-
9579
public async dispose(): Promise<void> {
9680
if (this.reporter) {
9781
await this.reporter.dispose();
@@ -102,13 +86,3 @@ class ExtensionTelemetry {
10286
}
10387
}
10488
}
105-
106-
function generateMachineId(): string {
107-
return crypto.createHash("sha256").update(os.hostname()).digest("base64");
108-
}
109-
110-
function generateSessionId(): string {
111-
return crypto.randomBytes(16).toString("hex");
112-
}
113-
114-
export const Telemetry = new ExtensionTelemetry();

test/suite/commands/serverCommand.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ describe("serverCommand", () => {
206206
validationHostnameStub,
207207
validationPortStub: sinon.SinonStub;
208208
beforeEach(() => {
209+
ext.telemetry = {
210+
sendEvent: sinon.stub(),
211+
} as any;
212+
209213
kdbData = {
210214
serverName: "testServer",
211215
serverAlias: "testServerAlias",

test/suite/utils/queryUtils.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,12 @@ describe("queryUtils", () => {
673673
});
674674

675675
describe("notifyExecution", () => {
676+
beforeEach(() => {
677+
ext.telemetry = {
678+
sendEvent: sinon.stub(),
679+
} as any;
680+
});
681+
676682
describe("repl", () => {
677683
describe("File", () => {
678684
it("should return telemetry for q", () => {
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright (c) 1998-2026 KX Systems Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
5+
* License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
10+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
14+
import * as telemetryModule from "@vscode/extension-telemetry";
15+
import * as sinon from "sinon";
16+
import * as vscode from "vscode";
17+
18+
import { ExtensionTelemetry } from "../../../src/utils/telemetryClient";
19+
20+
describe("ExtensionTelemetry", () => {
21+
let telemetryReporterMock: sinon.SinonStubbedInstance<telemetryModule.TelemetryReporter>;
22+
let outputChannelMock: any;
23+
24+
beforeEach(() => {
25+
telemetryReporterMock = sinon.createStubInstance(
26+
telemetryModule.TelemetryReporter,
27+
);
28+
sinon
29+
.stub(telemetryModule, "TelemetryReporter")
30+
.returns(telemetryReporterMock as any);
31+
32+
outputChannelMock = {
33+
appendLine: sinon.spy(),
34+
dispose: sinon.stub().resolves(),
35+
};
36+
sinon.stub(vscode.window, "createOutputChannel").returns(outputChannelMock);
37+
});
38+
39+
afterEach(() => {
40+
sinon.restore();
41+
delete process.env.CODE_TEST;
42+
});
43+
44+
it("should verify sendTelemetryEvent was called correctly", () => {
45+
process.env.CODE_TEST = ""; // Production mode
46+
const ext = new ExtensionTelemetry("conn-string", true);
47+
const props = { category: "testing" };
48+
const measurements = { duration: 100 };
49+
50+
ext.sendEvent("testEvent", props, measurements);
51+
52+
sinon.assert.calledOnce(telemetryReporterMock.sendTelemetryEvent);
53+
sinon.assert.calledWith(
54+
telemetryReporterMock.sendTelemetryEvent,
55+
"testEvent",
56+
sinon.match(props),
57+
sinon.match(measurements),
58+
);
59+
});
60+
61+
it("should verify sendTelemetryErrorEvent was called correctly", () => {
62+
process.env.CODE_TEST = "";
63+
const ext = new ExtensionTelemetry("conn-string", true);
64+
const testError = new Error("critical failure");
65+
testError.name = "DatabaseError";
66+
67+
ext.sendError(testError, { userRole: "admin" });
68+
69+
sinon.assert.calledOnce(telemetryReporterMock.sendTelemetryErrorEvent);
70+
sinon.assert.calledWith(
71+
telemetryReporterMock.sendTelemetryErrorEvent,
72+
"DatabaseError",
73+
sinon.match({
74+
name: "DatabaseError",
75+
message: "critical failure",
76+
userRole: "admin",
77+
}),
78+
);
79+
});
80+
81+
it("should verify output channel logging in test mode", () => {
82+
process.env.CODE_TEST = "true"; // Test mode
83+
const ext = new ExtensionTelemetry("conn-string", true);
84+
85+
ext.sendEvent("clickAction", { button: "left" });
86+
87+
sinon.assert.calledOnce(outputChannelMock.appendLine);
88+
sinon.assert.calledWithMatch(
89+
outputChannelMock.appendLine,
90+
/telemetry\/clickAction/,
91+
);
92+
});
93+
94+
it("should verify nothing is called when telemetry is disabled", () => {
95+
const ext = new ExtensionTelemetry("conn-string", false);
96+
97+
ext.sendEvent("unusedEvent");
98+
99+
sinon.assert.notCalled(telemetryReporterMock.sendTelemetryEvent);
100+
sinon.assert.notCalled(outputChannelMock.appendLine);
101+
});
102+
});

0 commit comments

Comments
 (0)