Skip to content

Commit ed87884

Browse files
authored
Support custom fetchers + fix version injection (#233)
1 parent f68d1a3 commit ed87884

File tree

5 files changed

+74
-10
lines changed

5 files changed

+74
-10
lines changed

dist/telemetryReporter.d.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,26 @@ export interface ReplacementOption {
2626
replacementString?: string;
2727
}
2828

29+
/**
30+
* A custom fetcher function that can be used to send telemetry data.
31+
* Compatible with the Node.js fetch API signature.
32+
* @param url The URL to send the request to
33+
* @param init The request initialization options including method, headers, and body
34+
* @returns A promise that resolves to a Response object
35+
*/
36+
export type CustomFetcher = (
37+
url: string,
38+
init?: { method: "POST"; headers?: Record<string, string>; body?: string }
39+
) => Promise<{text: () => Promise<string>; status: number; headers: Iterable<[string, string]>}>;
40+
2941
export class TelemetryReporter {
3042
/**
3143
* @param connectionString The app insights connection string
3244
* @param replacementOptions A list of replacement options for the app insights client. This allows the sender to filter out any sensitive or unnecessary information from the telemetry server.
3345
* @param initializationOptions Options for configuring the telemetry reporter, including additional common properties to be sent with each event.
46+
* @param customFetcher An optional custom fetcher function to use for sending telemetry data. If not provided, the default HTTPS module will be used. Compatible with Node.js fetch API.
3447
*/
35-
constructor(connectionString: string, replacementOptions?: ReplacementOption[], initializationOptions?: import("vscode").TelemetryLoggerOptions);
48+
constructor(connectionString: string, replacementOptions?: ReplacementOption[], initializationOptions?: import("vscode").TelemetryLoggerOptions, customFetcher?: CustomFetcher);
3649

3750
/**
3851
* A string representation of the current level of telemetry being collected

injectVersion.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@ const baseUtil = fs.readFileSync('./dist/node/common/util.js', 'utf8');
1111
const newBaseUtil = baseUtil.replace(/PACKAGE_JSON_VERSION/g, packageJson.version);
1212
fs.writeFileSync('./dist/node/common/util.js', newBaseUtil);
1313

14+
// Reads dist/node-esm/common/util.js and replaces PACKAGE_JSON_VERSION with the number from the package.json
15+
const baseUtilEsm = fs.readFileSync('./dist/node-esm/common/util.js', 'utf8');
16+
const newBaseUtilEsm = baseUtilEsm.replace(/PACKAGE_JSON_VERSION/g, packageJson.version);
17+
fs.writeFileSync('./dist/node-esm/common/util.js', newBaseUtilEsm);
18+
1419
// Reads dist/browser/common/util.js and replaces PACKAGE_JSON_VERSION with the number from the package.json
1520
const baseUtilBrowser = fs.readFileSync('./dist/browser/common/util.js', 'utf8');
1621
const newBaseUtilBrowser = baseUtilBrowser.replace(/PACKAGE_JSON_VERSION/g, packageJson.version);
17-
fs.writeFileSync('./dist/browser/common/util.js', newBaseUtilBrowser);
22+
fs.writeFileSync('./dist/browser/common/util.js', newBaseUtilBrowser);

package-lock.json

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

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@vscode/extension-telemetry",
33
"description": "A module for Visual Studio Code extensions to report consistent telemetry.",
4-
"version": "1.1.0",
4+
"version": "1.2.0",
55
"author": {
66
"name": "Microsoft Corporation"
77
},

src/node/telemetryReporter.ts

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,23 @@ import { BaseTelemetryReporter, ReplacementOption } from "../common/baseTelemetr
1212
import { BaseTelemetrySender } from "../common/baseTelemetrySender";
1313
import { TelemetryUtil } from "../common/util";
1414

15+
/**
16+
* A custom fetcher function that can be used to send telemetry data.
17+
* Compatible with the Node.js fetch API signature.
18+
* @param url The URL to send the request to
19+
* @param init The request initialization options including method, headers, and body
20+
* @returns A promise that resolves to a Response object
21+
*/
22+
export type CustomFetcher = (
23+
url: string,
24+
init?: { method: "POST"; headers?: Record<string, string>; body?: string }
25+
) => Promise<{text: () => Promise<string>; status: number; headers: Iterable<[string, string]>}>;
26+
1527
/**
1628
* Create a replacement for the XHTMLRequest object utilizing nodes HTTP module.
1729
* @returns A XHR override object used to override the XHTMLRequest object in the 1DS SDK
1830
*/
19-
function getXHROverride() {
31+
function getDefaultXHROverride(): IXHROverride {
2032
// Override the way events get sent since node doesn't have XHTMLRequest
2133
const customHttpXHROverride: IXHROverride = {
2234
sendPOST: (payload: IPayloadData, oncomplete) => {
@@ -31,7 +43,7 @@ function getXHROverride() {
3143
try {
3244
const req = https.request(payload.urlString, options, res => {
3345
res.on("data", function (responseData) {
34-
oncomplete(res.statusCode ?? 200, res.headers as Record<string, any>, responseData.toString());
46+
oncomplete(res.statusCode ?? 200, res.headers as Record<string, string>, responseData.toString());
3547
});
3648
// On response with error send status of 0 and a blank response to oncomplete so we can retry events
3749
res.on("error", function () {
@@ -53,12 +65,46 @@ function getXHROverride() {
5365
return customHttpXHROverride;
5466
}
5567

68+
/**
69+
* Create an XHR override from a custom fetcher function.
70+
* @param fetcher The custom fetcher function to use for sending telemetry data
71+
* @returns A XHR override object used to override the XHTMLRequest object in the 1DS SDK
72+
*/
73+
function createXHROverrideFromFetcher(fetcher: CustomFetcher): IXHROverride {
74+
const xhrOverride: IXHROverride = {
75+
sendPOST: (payload: IPayloadData, oncomplete) => {
76+
const dataString = typeof payload.data === "string" ? payload.data : Buffer.from(payload.data).toString();
77+
78+
fetcher(payload.urlString, { method: "POST", headers: payload.headers, body: dataString })
79+
.then(async (response) => {
80+
const responseHeaders: Record<string, string> = {};
81+
for (const [key, value] of response.headers) {
82+
responseHeaders[key] = value;
83+
}
84+
const body = await response.text();
85+
oncomplete(response.status, responseHeaders, body);
86+
})
87+
.catch(() => {
88+
// If it errors out, send status of 0 and a blank response to oncomplete so we can retry events
89+
oncomplete(0, {});
90+
});
91+
}
92+
};
93+
return xhrOverride;
94+
}
95+
5696
export class TelemetryReporter extends BaseTelemetryReporter {
57-
constructor(connectionString: string, replacementOptions?: ReplacementOption[], initializationOptions?: vscode.TelemetryLoggerOptions) {
58-
let clientFactory = (connectionString: string) => appInsightsClientFactory(connectionString, vscode.env.machineId, vscode.env.sessionId, getXHROverride(), replacementOptions);
97+
constructor(
98+
connectionString: string,
99+
replacementOptions?: ReplacementOption[],
100+
initializationOptions?: vscode.TelemetryLoggerOptions,
101+
customFetcher?: CustomFetcher
102+
) {
103+
const xhrOverride = customFetcher ? createXHROverrideFromFetcher(customFetcher) : getDefaultXHROverride();
104+
let clientFactory = (connectionString: string) => appInsightsClientFactory(connectionString, vscode.env.machineId, vscode.env.sessionId, xhrOverride, replacementOptions);
59105
// If connection string is usable by 1DS use the 1DS SDk
60106
if (TelemetryUtil.shouldUseOneDataSystemSDK(connectionString)) {
61-
clientFactory = (key: string) => oneDataSystemClientFactory(key, vscode, getXHROverride());
107+
clientFactory = (key: string) => oneDataSystemClientFactory(key, vscode, xhrOverride);
62108
}
63109

64110
const osShim = {

0 commit comments

Comments
 (0)