Add survey event (#26455)

* Start adding survey event

* Add surveyReady event

* Remove old notes

* Move event, simplify type, add test

1. Move the survey event to sendProjectTelemetry so that it happens on
open instead of on editing tsconfig.
2. Remove URL from the survey type; VS code should control this
information.
3. Add test based on large files event test. I'm not sure it's in the
right place, though.

* Fix tests and update API baseline

* Split survey sending from telemetry

Based on tests requested during review.

* Add additional assertion
This commit is contained in:
Nathan Shively-Sanders 2018-08-28 19:57:39 -07:00 committed by GitHub
parent cff04e6050
commit 30f611b055
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 185 additions and 3 deletions

View file

@ -5,6 +5,7 @@ namespace ts.server {
// tslint:disable variable-name
export const ProjectsUpdatedInBackgroundEvent = "projectsUpdatedInBackground";
export const SurveyReady = "surveyReady";
export const LargeFileReferencedEvent = "largeFileReferenced";
export const ConfigFileDiagEvent = "configFileDiag";
export const ProjectLanguageServiceStateEvent = "projectLanguageServiceState";
@ -17,6 +18,11 @@ namespace ts.server {
data: { openFiles: string[]; };
}
export interface SurveyReady {
eventName: typeof SurveyReady;
data: { surveyId: string; };
}
export interface LargeFileReferencedEvent {
eventName: typeof LargeFileReferencedEvent;
data: { file: string; fileSize: number; maxFileSize: number; };
@ -98,7 +104,7 @@ namespace ts.server {
readonly checkJs: boolean;
}
export type ProjectServiceEvent = LargeFileReferencedEvent | ProjectsUpdatedInBackgroundEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent | OpenFileInfoTelemetryEvent;
export type ProjectServiceEvent = LargeFileReferencedEvent | SurveyReady | ProjectsUpdatedInBackgroundEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent | OpenFileInfoTelemetryEvent;
export type ProjectServiceEventHandler = (event: ProjectServiceEvent) => void;
@ -462,6 +468,9 @@ namespace ts.server {
/** Tracks projects that we have already sent telemetry for. */
private readonly seenProjects = createMap<true>();
/** Tracks projects that we have already sent survey events for. */
private readonly seenSurveyProjects = createMap<true>();
/*@internal*/
readonly watchFactory: WatchFactory<WatchType, Project>;
@ -663,6 +672,14 @@ namespace ts.server {
this.eventHandler(event);
}
/* @internal */
sendSurveyReadyEvent(surveyId: string) {
if (!this.eventHandler) {
return;
}
this.eventHandler({ eventName: SurveyReady, data: { surveyId } });
}
/* @internal */
sendLargeFileReferencedEvent(file: string, fileSize: number) {
if (!this.eventHandler) {
@ -1477,6 +1494,20 @@ namespace ts.server {
return project;
}
/*@internal*/
sendSurveyReady(project: ExternalProject | ConfiguredProject): void {
if (this.seenSurveyProjects.has(project.projectName)) {
return;
}
if (project.getCompilerOptions().checkJs !== undefined) {
const name = "checkJs";
this.logger.info(`Survey ${name} is ready`);
this.sendSurveyReadyEvent(name);
this.seenSurveyProjects.set(project.projectName, true);
}
}
/*@internal*/
sendProjectTelemetry(project: ExternalProject | ConfiguredProject): void {
if (this.seenProjects.has(project.projectName)) {

View file

@ -1366,6 +1366,7 @@ namespace ts.server {
result = super.updateGraph();
}
this.projectService.sendProjectTelemetry(this);
this.projectService.sendSurveyReady(this);
return result;
}
@ -1570,6 +1571,7 @@ namespace ts.server {
updateGraph() {
const result = super.updateGraph();
this.projectService.sendProjectTelemetry(this);
this.projectService.sendSurveyReady(this);
return result;
}

View file

@ -2436,6 +2436,18 @@ namespace ts.server.protocol {
openFiles: string[];
}
export type SurveyReadyEventName = "surveyReady";
export interface SurveyReadyEvent extends Event {
event: SurveyReadyEventName;
body: SurveyReadyEventBody;
}
export interface SurveyReadyEventBody {
/** Name of the survey. This is an internal machine- and programmer-friendly name */
surveyId: string;
}
export type LargeFileReferencedEventName = "largeFileReferenced";
export interface LargeFileReferencedEvent extends Event {
event: LargeFileReferencedEventName;

View file

@ -572,6 +572,10 @@ namespace ts.server {
diagnostics: bakedDiags
}, "configFileDiag");
break;
case SurveyReady:
const { surveyId } = event.data;
this.event<protocol.SurveyReadyEventBody>({ surveyId }, "surveyReady");
break;
case ProjectLanguageServiceStateEvent: {
const eventName: protocol.ProjectLanguageServiceStateEventName = "projectLanguageServiceState";
this.event<protocol.ProjectLanguageServiceStateEventBody>({

View file

@ -2863,7 +2863,7 @@ namespace ts.projectSystem {
const session = createSession(host, {
canUseEvents: true,
eventHandler: e => {
if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ProjectsUpdatedInBackgroundEvent || e.eventName === server.ProjectInfoTelemetryEvent || e.eventName === server.OpenFileInfoTelemetryEvent || e.eventName === server.LargeFileReferencedEvent) {
if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ProjectsUpdatedInBackgroundEvent || e.eventName === server.ProjectInfoTelemetryEvent || e.eventName === server.OpenFileInfoTelemetryEvent || e.eventName === server.LargeFileReferencedEvent || e.eventName === server.SurveyReady) {
return;
}
assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent);
@ -3539,6 +3539,121 @@ namespace ts.projectSystem {
}
});
function createSessionWithEventHandler(host: TestServerHost) {
const surveyEvents: server.SurveyReady[] = [];
const session = createSession(host, {
eventHandler: e => {
if (e.eventName === server.SurveyReady) {
surveyEvents.push(e);
}
}
});
return { session, verifySurveyReadyEvent };
function verifySurveyReadyEvent(numberOfEvents: number) {
assert.equal(surveyEvents.length, numberOfEvents);
const expectedEvents = numberOfEvents === 0 ? [] : [{
eventName: server.SurveyReady,
data: { surveyId: "checkJs" }
}];
assert.deepEqual(surveyEvents, expectedEvents);
}
}
it("doesn't log an event when checkJs isn't set", () => {
const projectRoot = "/user/username/projects/project";
const file: File = {
path: `${projectRoot}/src/file.ts`,
content: "export var y = 10;"
};
const tsconfig: File = {
path: `${projectRoot}/tsconfig.json`,
content: JSON.stringify({ compilerOptions: { } }),
};
const host = createServerHost([file, tsconfig]);
const { session, verifySurveyReadyEvent } = createSessionWithEventHandler(host);
const service = session.getProjectService();
openFilesForSession([file], session);
checkNumberOfProjects(service, { configuredProjects: 1 });
const project = service.configuredProjects.get(tsconfig.path)!;
checkProjectActualFiles(project, [file.path, tsconfig.path]);
verifySurveyReadyEvent(0);
});
it("logs an event when checkJs is set", () => {
const projectRoot = "/user/username/projects/project";
const file: File = {
path: `${projectRoot}/src/file.ts`,
content: "export var y = 10;"
};
const tsconfig: File = {
path: `${projectRoot}/tsconfig.json`,
content: JSON.stringify({ compilerOptions: { checkJs: true } }),
};
const host = createServerHost([file, tsconfig]);
const { session, verifySurveyReadyEvent } = createSessionWithEventHandler(host);
openFilesForSession([file], session);
verifySurveyReadyEvent(1);
});
it("logs an event when checkJs is set, only the first time", () => {
const projectRoot = "/user/username/projects/project";
const file: File = {
path: `${projectRoot}/src/file.ts`,
content: "export var y = 10;"
};
const rando: File = {
path: `/rando/calrissian.ts`,
content: "export function f() { }"
};
const tsconfig: File = {
path: `${projectRoot}/tsconfig.json`,
content: JSON.stringify({ compilerOptions: { checkJs: true } }),
};
const host = createServerHost([file, tsconfig]);
const { session, verifySurveyReadyEvent } = createSessionWithEventHandler(host);
openFilesForSession([file], session);
verifySurveyReadyEvent(1);
closeFilesForSession([file], session);
openFilesForSession([rando], session);
openFilesForSession([file], session);
verifySurveyReadyEvent(1);
});
it("logs an event when checkJs is set after closing and reopening", () => {
const projectRoot = "/user/username/projects/project";
const file: File = {
path: `${projectRoot}/src/file.ts`,
content: "export var y = 10;"
};
const rando: File = {
path: `/rando/calrissian.ts`,
content: "export function f() { }"
};
const tsconfig: File = {
path: `${projectRoot}/tsconfig.json`,
content: JSON.stringify({ }),
};
const host = createServerHost([file, tsconfig]);
const { session, verifySurveyReadyEvent } = createSessionWithEventHandler(host);
openFilesForSession([file], session);
verifySurveyReadyEvent(0);
closeFilesForSession([file], session);
openFilesForSession([rando], session);
host.writeFile(tsconfig.path, JSON.stringify({ compilerOptions: { checkJs: true } }));
openFilesForSession([file], session);
verifySurveyReadyEvent(1);
});
describe("CompileOnSaveAffectedFileListRequest with and without projectFileName in request", () => {
const projectRoot = "/user/username/projects/myproject";
const core: File = {

View file

@ -7462,6 +7462,15 @@ declare namespace ts.server.protocol {
*/
openFiles: string[];
}
type SurveyReadyEventName = "surveyReady";
interface SurveyReadyEvent extends Event {
event: SurveyReadyEventName;
body: SurveyReadyEventBody;
}
interface SurveyReadyEventBody {
/** Name of the survey. This is an internal machine- and programmer-friendly name */
surveyId: string;
}
type LargeFileReferencedEventName = "largeFileReferenced";
interface LargeFileReferencedEvent extends Event {
event: LargeFileReferencedEventName;
@ -8204,6 +8213,7 @@ declare namespace ts.server {
declare namespace ts.server {
const maxProgramSizeForNonTsFiles: number;
const ProjectsUpdatedInBackgroundEvent = "projectsUpdatedInBackground";
const SurveyReady = "surveyReady";
const LargeFileReferencedEvent = "largeFileReferenced";
const ConfigFileDiagEvent = "configFileDiag";
const ProjectLanguageServiceStateEvent = "projectLanguageServiceState";
@ -8215,6 +8225,12 @@ declare namespace ts.server {
openFiles: string[];
};
}
interface SurveyReady {
eventName: typeof SurveyReady;
data: {
surveyId: string;
};
}
interface LargeFileReferencedEvent {
eventName: typeof LargeFileReferencedEvent;
data: {
@ -8293,7 +8309,7 @@ declare namespace ts.server {
interface OpenFileInfo {
readonly checkJs: boolean;
}
type ProjectServiceEvent = LargeFileReferencedEvent | ProjectsUpdatedInBackgroundEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent | OpenFileInfoTelemetryEvent;
type ProjectServiceEvent = LargeFileReferencedEvent | SurveyReady | ProjectsUpdatedInBackgroundEvent | ConfigFileDiagEvent | ProjectLanguageServiceStateEvent | ProjectInfoTelemetryEvent | OpenFileInfoTelemetryEvent;
type ProjectServiceEventHandler = (event: ProjectServiceEvent) => void;
interface SafeList {
[name: string]: {
@ -8412,6 +8428,8 @@ declare namespace ts.server {
readonly syntaxOnly?: boolean;
/** Tracks projects that we have already sent telemetry for. */
private readonly seenProjects;
/** Tracks projects that we have already sent survey events for. */
private readonly seenSurveyProjects;
constructor(opts: ProjectServiceOptions);
toPath(fileName: string): Path;
private loadTypesMap;