Skip to content

Commit

Permalink
Sync way to determine caller extension (#14155)
Browse files Browse the repository at this point in the history
  • Loading branch information
DonJayamanne authored Aug 21, 2023
1 parent 41f65d7 commit 58caf39
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 1 deletion.
11 changes: 11 additions & 0 deletions src/gdpr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,17 @@
"${include}": [
"${F1}"
]
}
*/
//Telemetry.ExtensionCallerIdentification
/* __GDPR__
"DATASCIENCE.JUPYTER_EXTENSION_CALLER_IDENTIFICATION" : {
"extensionId": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Extension Id that's attempting to use the API.","owner":"donjayamanne"},
"result": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"","owner":"donjayamanne"},
"${include}": [
"${F1}"
]
}
*/
Expand Down
63 changes: 62 additions & 1 deletion src/platform/common/application/extensions.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import { DataScience } from '../utils/localize';
import { EXTENSION_ROOT_DIR } from '../../constants.node';
import { IFileSystem } from '../platform/types';
import { parseStack } from '../../errors';
import { unknownExtensionId } from '../constants';
import { JVSC_EXTENSION_ID, Telemetry, unknownExtensionId } from '../constants';
import { traceError } from '../../logging';
import { sendTelemetryEvent } from '../../telemetry';

/**
* Provides functions for tracking the list of extensions that VS code has installed (besides our own)
Expand All @@ -33,6 +34,7 @@ export class Extensions implements IExtensions {
}
public async determineExtensionFromCallStack(): Promise<{ extensionId: string; displayName: string }> {
const stack = new Error().stack;
const syncId = this.determineExtensionFromCallStackSync(stack || '');
if (stack) {
const jupyterExtRoot = path.join(EXTENSION_ROOT_DIR.toLowerCase(), path.sep);
const frames = stack
Expand Down Expand Up @@ -76,6 +78,23 @@ export class Extensions implements IExtensions {
(frame.startsWith(matchingExt.extensionUri.toString()) ||
frame.startsWith(matchingExt.extensionUri.fsPath.toString()))
) {
if (
syncId.extensionId === unknownExtensionId &&
matchingExt.id !== unknownExtensionId
) {
sendTelemetryEvent(Telemetry.ExtensionCallerIdentification, undefined, {
extensionId: matchingExt.id,
result: 'WorkedOnlyInAsync'
});
} else if (
syncId.extensionId === unknownExtensionId &&
syncId.extensionId === matchingExt.id
) {
sendTelemetryEvent(Telemetry.ExtensionCallerIdentification, undefined, {
extensionId: matchingExt.id,
result: 'WorkedInBoth'
});
}
return {
extensionId: matchingExt.id,
displayName: matchingExt.packageJSON.displayName
Expand All @@ -90,8 +109,50 @@ export class Extensions implements IExtensions {
dirName = path.dirname(dirName);
}
}
if (syncId.extensionId !== unknownExtensionId) {
sendTelemetryEvent(Telemetry.ExtensionCallerIdentification, undefined, {
extensionId: syncId.extensionId,
result: 'WorkedOnlyInSync'
});
}
traceError(`Unable to determine the caller of the extension API for trace stack.`, stack);
}
return { extensionId: unknownExtensionId, displayName: DataScience.unknownPackage };
}
private determineExtensionFromCallStackSync(stack: string): { extensionId: string; displayName: string } {
try {
if (stack) {
const jupyterExtRoot = this.getExtension(JVSC_EXTENSION_ID)!.extensionUri.toString().toLowerCase();
const frames = stack
.split('\n')
.map((f) => {
const result = /\((.*)\)/.exec(f);
if (result) {
return result[1];
}
})
// Since this is web, look for paths that start with http (which also includes https).
.filter((item) => item && item.toLowerCase().startsWith('http'))
.filter((item) => item && !item.toLowerCase().startsWith(jupyterExtRoot)) as string[];
parseStack(new Error('Ex')).forEach((item) => {
const fileName = item.getFileName();
if (fileName && !fileName.toLowerCase().startsWith(jupyterExtRoot)) {
frames.push(fileName);
}
});
for (const frame of frames) {
const matchingExt = this.all.find(
(ext) => ext.id !== JVSC_EXTENSION_ID && frame.startsWith(ext.extensionUri.toString())
);
if (matchingExt) {
return { extensionId: matchingExt.id, displayName: matchingExt.packageJSON.displayName };
}
}
}
return { extensionId: unknownExtensionId, displayName: DataScience.unknownPackage };
} catch {
return { extensionId: unknownExtensionId, displayName: DataScience.unknownPackage };
//
}
}
}
1 change: 1 addition & 0 deletions src/platform/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ export enum Telemetry {
CellOutputMimeType = 'DS_INTERNAL.CELL_OUTPUT_MIME_TYPE',
JupyterApiUsage = 'DATASCIENCE.JUPYTER_API_USAGE',
JupyterServerProviderResponseApi = 'DATASCIENCE.JUPYTER_SERVER_PROVIDER_RESPONSE_API',
ExtensionCallerIdentification = 'DATASCIENCE.JUPYTER_EXTENSION_CALLER_IDENTIFICATION',
JupyterKernelApiUsage = 'DATASCIENCE.JUPYTER_KERNEL_API_USAGE',
JupyterKernelApiAccess = 'DATASCIENCE.JUPYTER_KERNEL_API_ACCESS',
JupyterKernelStartupHook = 'DATASCIENCE.JUPYTER_KERNEL_STARTUP_HOOK',
Expand Down
24 changes: 24 additions & 0 deletions src/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3523,6 +3523,30 @@ export class IEventNamePropertyMapping {
}
}
};
/**
* Telemetry sent when an extension uses our 3rd party Kernel API.
*/
[Telemetry.ExtensionCallerIdentification]: TelemetryEventInfo<{
/**
* Extension Id that's attempting to use the API.
*/
extensionId: string;
result: 'WorkedInBoth' | 'WorkedOnlyInSync' | 'WorkedOnlyInAsync';
}> = {
owner: 'donjayamanne',
feature: 'N/A',
source: 'N/A',
properties: {
extensionId: {
classification: 'PublicNonPersonalData',
purpose: 'FeatureInsight'
},
result: {
classification: 'PublicNonPersonalData',
purpose: 'FeatureInsight'
}
}
};
/**
* Telemetry sent when an extension uses our 3rd party Kernel API.
*/
Expand Down

0 comments on commit 58caf39

Please sign in to comment.