Skip to content

Commit 8b2a3b1

Browse files
authored
Query history: Add new VariantAnalysisHistoryItem type (#1590)
1 parent fad5bb3 commit 8b2a3b1

File tree

7 files changed

+124
-38
lines changed

7 files changed

+124
-38
lines changed

extensions/ql-vscode/src/history-item-label-provider.ts

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { QueryHistoryConfig } from './config';
44
import { LocalQueryInfo, QueryHistoryInfo } from './query-results';
55
import { RemoteQueryHistoryItem } from './remote-queries/remote-query-history-item';
66
import { pluralize } from './helpers';
7+
import { VariantAnalysisHistoryItem } from './remote-queries/variant-analysis-history-item';
8+
import { assertNever } from './pure/helpers-pure';
79

810
interface InterpolateReplacements {
911
t: string; // Start time
@@ -21,9 +23,20 @@ export class HistoryItemLabelProvider {
2123
}
2224

2325
getLabel(item: QueryHistoryInfo) {
24-
const replacements = item.t === 'local'
25-
? this.getLocalInterpolateReplacements(item)
26-
: this.getRemoteInterpolateReplacements(item);
26+
let replacements: InterpolateReplacements;
27+
switch (item.t) {
28+
case 'local':
29+
replacements = this.getLocalInterpolateReplacements(item);
30+
break;
31+
case 'remote':
32+
replacements = this.getRemoteInterpolateReplacements(item);
33+
break;
34+
case 'variant-analysis':
35+
replacements = this.getVariantAnalysisInterpolateReplacements(item);
36+
break;
37+
default:
38+
assertNever(item);
39+
}
2740

2841
const rawLabel = item.userSpecifiedLabel ?? (this.config.format || '%q');
2942

@@ -37,11 +50,20 @@ export class HistoryItemLabelProvider {
3750
* @returns the name of the query, unless there is a custom label for this query.
3851
*/
3952
getShortLabel(item: QueryHistoryInfo): string {
40-
return item.userSpecifiedLabel
41-
? this.getLabel(item)
42-
: item.t === 'local'
43-
? item.getQueryName()
44-
: item.remoteQuery.queryName;
53+
if (item.userSpecifiedLabel) {
54+
return this.getLabel(item);
55+
} else {
56+
switch (item.t) {
57+
case 'local':
58+
return item.getQueryName();
59+
case 'remote':
60+
return item.remoteQuery.queryName;
61+
case 'variant-analysis':
62+
return item.variantAnalysis.query.name;
63+
default:
64+
assertNever(item);
65+
}
66+
}
4567
}
4668

4769

@@ -90,4 +112,17 @@ export class HistoryItemLabelProvider {
90112
'%': '%'
91113
};
92114
}
115+
116+
private getVariantAnalysisInterpolateReplacements(item: VariantAnalysisHistoryItem): InterpolateReplacements {
117+
const resultCount = item.resultCount ? `(${pluralize(item.resultCount, 'result', 'results')})` : '';
118+
return {
119+
t: new Date(item.variantAnalysis.executionStartTime).toLocaleString(env.language),
120+
q: `${item.variantAnalysis.query.name} (${item.variantAnalysis.query.language})`,
121+
d: 'TODO',
122+
r: resultCount,
123+
s: item.status,
124+
f: path.basename(item.variantAnalysis.query.filePath),
125+
'%': '%',
126+
};
127+
}
93128
}

extensions/ql-vscode/src/query-history.ts

Lines changed: 57 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -206,13 +206,9 @@ export class HistoryTreeDataProvider extends DisposableObject implements TreeDat
206206
const h1Label = this.labelProvider.getLabel(h1).toLowerCase();
207207
const h2Label = this.labelProvider.getLabel(h2).toLowerCase();
208208

209-
const h1Date = h1.t === 'local'
210-
? h1.initialInfo.start.getTime()
211-
: h1.remoteQuery?.executionStartTime;
209+
const h1Date = this.getItemDate(h1);
212210

213-
const h2Date = h2.t === 'local'
214-
? h2.initialInfo.start.getTime()
215-
: h2.remoteQuery?.executionStartTime;
211+
const h2Date = this.getItemDate(h2);
216212

217213
const resultCount1 = h1.t === 'local'
218214
? h1.completedQuery?.resultCount ?? -1
@@ -311,6 +307,19 @@ export class HistoryTreeDataProvider extends DisposableObject implements TreeDat
311307
this._sortOrder = newSortOrder;
312308
this._onDidChangeTreeData.fire(undefined);
313309
}
310+
311+
private getItemDate(item: QueryHistoryInfo) {
312+
switch (item.t) {
313+
case 'local':
314+
return item.initialInfo.start.getTime();
315+
case 'remote':
316+
return item.remoteQuery.executionStartTime;
317+
case 'variant-analysis':
318+
return item.variantAnalysis.executionStartTime;
319+
default:
320+
assertNever(item);
321+
}
322+
}
314323
}
315324

316325
export class QueryHistoryManager extends DisposableObject {
@@ -649,10 +658,20 @@ export class QueryHistoryManager extends DisposableObject {
649658
return;
650659
}
651660

652-
const queryPath = finalSingleItem.t === 'local'
653-
? finalSingleItem.initialInfo.queryPath
654-
: finalSingleItem.remoteQuery.queryFilePath;
655-
661+
let queryPath: string;
662+
switch (finalSingleItem.t) {
663+
case 'local':
664+
queryPath = finalSingleItem.initialInfo.queryPath;
665+
break;
666+
case 'remote':
667+
queryPath = finalSingleItem.remoteQuery.queryFilePath;
668+
break;
669+
case 'variant-analysis':
670+
queryPath = finalSingleItem.variantAnalysis.query.filePath;
671+
break;
672+
default:
673+
assertNever(finalSingleItem);
674+
}
656675
const textDocument = await workspace.openTextDocument(
657676
Uri.file(queryPath)
658677
);
@@ -710,8 +729,12 @@ export class QueryHistoryManager extends DisposableObject {
710729
// We need to delete it from disk as well.
711730
await item.completedQuery?.query.deleteQuery();
712731
}
713-
} else {
732+
} else if (item.t === 'remote') {
714733
await this.removeRemoteQuery(item);
734+
} else if (item.t === 'variant-analysis') {
735+
// TODO
736+
} else {
737+
assertNever(item);
715738
}
716739
}));
717740

@@ -1025,15 +1048,20 @@ export class QueryHistoryManager extends DisposableObject {
10251048
isQuickEval: String(!!(finalSingleItem.t === 'local' && finalSingleItem.initialInfo.quickEvalPosition)),
10261049
queryText: encodeURIComponent(await this.getQueryText(finalSingleItem)),
10271050
});
1028-
const queryId = finalSingleItem.t === 'local'
1029-
? finalSingleItem.initialInfo.id
1030-
: finalSingleItem.queryId;
10311051

1032-
const uri = Uri.parse(
1033-
`codeql:${queryId}?${params.toString()}`, true
1034-
);
1035-
const doc = await workspace.openTextDocument(uri);
1036-
await window.showTextDocument(doc, { preview: false });
1052+
if (finalSingleItem.t === 'variant-analysis') {
1053+
// TODO
1054+
} else {
1055+
const queryId = finalSingleItem.t === 'local'
1056+
? finalSingleItem.initialInfo.id
1057+
: finalSingleItem.queryId;
1058+
1059+
const uri = Uri.parse(
1060+
`codeql:${queryId}?${params.toString()}`, true
1061+
);
1062+
const doc = await workspace.openTextDocument(uri);
1063+
await window.showTextDocument(doc, { preview: false });
1064+
}
10371065
}
10381066

10391067
async handleViewSarifAlerts(
@@ -1149,9 +1177,16 @@ export class QueryHistoryManager extends DisposableObject {
11491177
}
11501178

11511179
async getQueryText(item: QueryHistoryInfo): Promise<string> {
1152-
return item.t === 'local'
1153-
? item.initialInfo.queryText
1154-
: item.remoteQuery.queryText;
1180+
switch (item.t) {
1181+
case 'local':
1182+
return item.initialInfo.queryText;
1183+
case 'remote':
1184+
return item.remoteQuery.queryText;
1185+
case 'variant-analysis':
1186+
return 'TODO';
1187+
default:
1188+
assertNever(item);
1189+
}
11551190
}
11561191

11571192
async handleExportResults(): Promise<void> {

extensions/ql-vscode/src/query-results.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { QueryStatus } from './query-status';
1919
import { RemoteQueryHistoryItem } from './remote-queries/remote-query-history-item';
2020
import { QueryEvaluationInfo, QueryWithResults } from './run-queries-shared';
2121
import { formatLegacyMessage } from './legacy-query-server/run-queries';
22+
import { VariantAnalysisHistoryItem } from './remote-queries/variant-analysis-history-item';
2223

2324
/**
2425
* query-results.ts
@@ -201,13 +202,13 @@ export function ensureMetadataIsComplete(metadata: QueryMetadata | undefined) {
201202
}
202203

203204
/**
204-
* Used in Interface and Compare-Interface for queries that we know have been complated.
205+
* Used in Interface and Compare-Interface for queries that we know have been completed.
205206
*/
206207
export type CompletedLocalQueryInfo = LocalQueryInfo & {
207208
completedQuery: CompletedQueryInfo
208209
};
209210

210-
export type QueryHistoryInfo = LocalQueryInfo | RemoteQueryHistoryItem;
211+
export type QueryHistoryInfo = LocalQueryInfo | RemoteQueryHistoryItem | VariantAnalysisHistoryItem;
211212

212213
export class LocalQueryInfo {
213214
readonly t = 'local';

extensions/ql-vscode/src/query-serialization.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ export async function slurpQueryHistory(fsPath: string): Promise<QueryHistoryInf
1515

1616
const data = await fs.readFile(fsPath, 'utf8');
1717
const obj = JSON.parse(data);
18-
if (obj.version !== 1) {
19-
void showAndLogErrorMessage(`Unsupported query history format: v${obj.version}. `);
18+
if (![1, 2].includes(obj.version)) {
19+
void showAndLogErrorMessage(`Can't parse query history. Unsupported query history format: v${obj.version}. `);
2020
return [];
2121
}
2222

@@ -54,7 +54,7 @@ export async function slurpQueryHistory(fsPath: string): Promise<QueryHistoryInf
5454
// most likely another workspace has deleted them because the
5555
// queries aged out.
5656
return asyncFilter(parsedQueries, async (q) => {
57-
if (q.t === 'remote') {
57+
if (q.t === 'remote' || q.t === 'variant-analysis') {
5858
// the slurper doesn't know where the remote queries are stored
5959
// so we need to assume here that they exist. Later, we check to
6060
// see if they exist on disk.
@@ -90,7 +90,7 @@ export async function splatQueryHistory(queries: QueryHistoryInfo[], fsPath: str
9090
// remove incomplete local queries since they cannot be recreated on restart
9191
const filteredQueries = queries.filter(q => q.t === 'local' ? q.completedQuery !== undefined : true);
9292
const data = JSON.stringify({
93-
version: 1,
93+
version: 2, // version 2 adds the `variant-analysis` type.
9494
queries: filteredQueries
9595
}, null, 2);
9696
await fs.writeFile(fsPath, data);
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { QueryStatus } from '../query-status';
2+
import { VariantAnalysis } from './shared/variant-analysis';
3+
4+
/**
5+
* Information about a variant analysis.
6+
*/
7+
export interface VariantAnalysisHistoryItem {
8+
readonly t: 'variant-analysis';
9+
failureReason?: string;
10+
resultCount?: number;
11+
status: QueryStatus;
12+
completed: boolean;
13+
variantAnalysis: VariantAnalysis;
14+
userSpecifiedLabel?: string;
15+
}

extensions/ql-vscode/src/vscode-tests/no-workspace/query-results.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ describe('query-results', () => {
290290
it('should handle an invalid query history version', async () => {
291291
const badPath = path.join(tmpDir.name, 'bad-query-history.json');
292292
fs.writeFileSync(badPath, JSON.stringify({
293-
version: 2,
293+
version: 3,
294294
queries: allHistory
295295
}), 'utf8');
296296

extensions/ql-vscode/src/vscode-tests/no-workspace/remote-queries/remote-query-history.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ describe('Remote queries and query history manager', function() {
164164

165165
// also, both queries should be removed from on disk storage
166166
expect(fs.readJSONSync(path.join(STORAGE_DIR, 'workspace-query-history.json'))).to.deep.eq({
167-
version: 1,
167+
version: 2,
168168
queries: []
169169
});
170170
});

0 commit comments

Comments
 (0)