Add hash of project file location to project info telemetry (#16397)

* Add hash of project file location to project info telemetry

* Rename to projectId
This commit is contained in:
Andy 2017-06-09 13:12:31 -07:00 committed by GitHub
parent 7796e3775a
commit a757e84284
7 changed files with 40 additions and 28 deletions

View file

@ -35,6 +35,10 @@ namespace ts {
getDirectories(path: string): string[];
readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[];
getModifiedTime?(path: string): Date;
/**
* This should be cryptographically secure.
* A good implementation is node.js' `crypto.createHash`. (https://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm)
*/
createHash?(data: string): string;
getMemoryUsage?(): number;
exit(exitCode?: number): void;

View file

@ -731,7 +731,7 @@ namespace Harness.LanguageService {
}
createHash(s: string) {
return s;
return mockHash(s);
}
require(_initialDir: string, _moduleName: string): ts.server.RequireResult {
@ -856,4 +856,8 @@ namespace Harness.LanguageService {
getClassifier(): ts.Classifier { throw new Error("getClassifier is not available using the server interface."); }
getPreProcessedFileInfo(): ts.PreProcessedFileInfo { throw new Error("getPreProcessedFileInfo is not available using the server interface."); }
}
export function mockHash(s: string): string {
return `hash-${s}`;
}
}

View file

@ -47,7 +47,7 @@ namespace ts {
clearTimeout,
setImmediate: typeof setImmediate !== "undefined" ? setImmediate : action => setTimeout(action, 0),
clearImmediate: typeof clearImmediate !== "undefined" ? clearImmediate : clearTimeout,
createHash: s => s
createHash: Harness.LanguageService.mockHash,
};
}

View file

@ -25,7 +25,7 @@ namespace ts.server {
clearTimeout: noop,
setImmediate: () => 0,
clearImmediate: noop,
createHash: s => s
createHash: Harness.LanguageService.mockHash,
};
const mockLogger: Logger = {

View file

@ -9,6 +9,7 @@ namespace ts.projectSystem {
et.service.openClientFile(file.path);
assert.equal(et.getEvents().length, 0);
});
it("only sends an event once", () => {
const file = makeFile("/a.ts");
const tsconfig = makeFile("/tsconfig.json", {});
@ -46,12 +47,13 @@ namespace ts.projectSystem {
const et = new EventTracker([file1]);
const compilerOptions: ts.CompilerOptions = { strict: true };
const projectFileName = "foo.csproj";
const projectFileName = "/hunter2/foo.csproj";
open();
// TODO: Apparently compilerOptions is mutated, so have to repeat it here!
et.assertProjectInfoTelemetryEvent({
projectId: Harness.LanguageService.mockHash("/hunter2/foo.csproj"),
compilerOptions: { strict: true },
compileOnSave: true,
// These properties can't be present for an external project, so they are undefined instead of false.
@ -195,6 +197,7 @@ namespace ts.projectSystem {
const et = new EventTracker([jsconfig, file]);
et.service.openClientFile(file.path);
et.assertProjectInfoTelemetryEvent({
projectId: Harness.LanguageService.mockHash("/jsconfig.json"),
fileStats: fileStats({ js: 1 }),
compilerOptions: autoJsCompilerOptions,
typeAcquisition: {
@ -214,6 +217,7 @@ namespace ts.projectSystem {
et.service.openClientFile(file.path);
et.getEvent<server.ProjectLanguageServiceStateEvent>(server.ProjectLanguageServiceStateEvent, /*mayBeMore*/ true);
et.assertProjectInfoTelemetryEvent({
projectId: Harness.LanguageService.mockHash("/jsconfig.json"),
fileStats: fileStats({ js: 1 }),
compilerOptions: autoJsCompilerOptions,
configFileName: "jsconfig.json",
@ -248,7 +252,26 @@ namespace ts.projectSystem {
}
assertProjectInfoTelemetryEvent(partial: Partial<server.ProjectInfoTelemetryEventData>): void {
assert.deepEqual(this.getEvent<server.ProjectInfoTelemetryEvent>(ts.server.ProjectInfoTelemetryEvent), makePayload(partial));
assert.deepEqual(this.getEvent<server.ProjectInfoTelemetryEvent>(ts.server.ProjectInfoTelemetryEvent), {
projectId: Harness.LanguageService.mockHash("/tsconfig.json"),
fileStats: fileStats({ ts: 1 }),
compilerOptions: {},
extends: false,
files: false,
include: false,
exclude: false,
compileOnSave: false,
typeAcquisition: {
enable: false,
exclude: false,
include: false,
},
configFileName: "tsconfig.json",
projectType: "configured",
languageServiceEnabled: true,
version: ts.version,
...partial,
});
}
getEvent<T extends server.ProjectServiceEvent>(eventName: T["eventName"], mayBeMore = false): T["data"] {
@ -260,28 +283,6 @@ namespace ts.projectSystem {
}
}
function makePayload(partial: Partial<server.ProjectInfoTelemetryEventData>): server.ProjectInfoTelemetryEventData {
return {
fileStats: fileStats({ ts: 1 }),
compilerOptions: {},
extends: false,
files: false,
include: false,
exclude: false,
compileOnSave: false,
typeAcquisition: {
enable: false,
exclude: false,
include: false,
},
configFileName: "tsconfig.json",
projectType: "configured",
languageServiceEnabled: true,
version: ts.version,
...partial
};
}
function makeFile(path: string, content: {} = ""): projectSystem.FileOrFolder {
return { path, content: typeof content === "string" ? "" : JSON.stringify(content) };
}

View file

@ -472,7 +472,7 @@ namespace ts.projectSystem {
}
createHash(s: string): string {
return s;
return Harness.LanguageService.mockHash(s);
}
triggerDirectoryWatcherCallback(directoryName: string, fileName: string): void {

View file

@ -37,6 +37,8 @@ namespace ts.server {
}
export interface ProjectInfoTelemetryEventData {
/** Cryptographically secure hash of project file location. */
readonly projectId: string;
/** Count of file extensions seen in the project. */
readonly fileStats: FileStats;
/**
@ -1049,6 +1051,7 @@ namespace ts.server {
if (!this.eventHandler) return;
const data: ProjectInfoTelemetryEventData = {
projectId: this.host.createHash(projectKey),
fileStats: countEachFileTypes(project.getScriptInfos()),
compilerOptions: convertCompilerOptionsForTelemetry(project.getCompilerOptions()),
typeAcquisition: convertTypeAcquisition(project.getTypeAcquisition()),