Query history: Add new VariantAnalysisHistoryItem type (#1590)

This commit is contained in:
Shati Patel
2022-10-14 10:04:56 +01:00
committed by GitHub
parent fad5bb31a0
commit 8b2a3b18ce
7 changed files with 124 additions and 38 deletions

View File

@@ -4,6 +4,8 @@ import { QueryHistoryConfig } from './config';
import { LocalQueryInfo, QueryHistoryInfo } from './query-results';
import { RemoteQueryHistoryItem } from './remote-queries/remote-query-history-item';
import { pluralize } from './helpers';
import { VariantAnalysisHistoryItem } from './remote-queries/variant-analysis-history-item';
import { assertNever } from './pure/helpers-pure';
interface InterpolateReplacements {
t: string; // Start time
@@ -21,9 +23,20 @@ export class HistoryItemLabelProvider {
}
getLabel(item: QueryHistoryInfo) {
const replacements = item.t === 'local'
? this.getLocalInterpolateReplacements(item)
: this.getRemoteInterpolateReplacements(item);
let replacements: InterpolateReplacements;
switch (item.t) {
case 'local':
replacements = this.getLocalInterpolateReplacements(item);
break;
case 'remote':
replacements = this.getRemoteInterpolateReplacements(item);
break;
case 'variant-analysis':
replacements = this.getVariantAnalysisInterpolateReplacements(item);
break;
default:
assertNever(item);
}
const rawLabel = item.userSpecifiedLabel ?? (this.config.format || '%q');
@@ -37,11 +50,20 @@ export class HistoryItemLabelProvider {
* @returns the name of the query, unless there is a custom label for this query.
*/
getShortLabel(item: QueryHistoryInfo): string {
return item.userSpecifiedLabel
? this.getLabel(item)
: item.t === 'local'
? item.getQueryName()
: item.remoteQuery.queryName;
if (item.userSpecifiedLabel) {
return this.getLabel(item);
} else {
switch (item.t) {
case 'local':
return item.getQueryName();
case 'remote':
return item.remoteQuery.queryName;
case 'variant-analysis':
return item.variantAnalysis.query.name;
default:
assertNever(item);
}
}
}
@@ -90,4 +112,17 @@ export class HistoryItemLabelProvider {
'%': '%'
};
}
private getVariantAnalysisInterpolateReplacements(item: VariantAnalysisHistoryItem): InterpolateReplacements {
const resultCount = item.resultCount ? `(${pluralize(item.resultCount, 'result', 'results')})` : '';
return {
t: new Date(item.variantAnalysis.executionStartTime).toLocaleString(env.language),
q: `${item.variantAnalysis.query.name} (${item.variantAnalysis.query.language})`,
d: 'TODO',
r: resultCount,
s: item.status,
f: path.basename(item.variantAnalysis.query.filePath),
'%': '%',
};
}
}

View File

@@ -206,13 +206,9 @@ export class HistoryTreeDataProvider extends DisposableObject implements TreeDat
const h1Label = this.labelProvider.getLabel(h1).toLowerCase();
const h2Label = this.labelProvider.getLabel(h2).toLowerCase();
const h1Date = h1.t === 'local'
? h1.initialInfo.start.getTime()
: h1.remoteQuery?.executionStartTime;
const h1Date = this.getItemDate(h1);
const h2Date = h2.t === 'local'
? h2.initialInfo.start.getTime()
: h2.remoteQuery?.executionStartTime;
const h2Date = this.getItemDate(h2);
const resultCount1 = h1.t === 'local'
? h1.completedQuery?.resultCount ?? -1
@@ -311,6 +307,19 @@ export class HistoryTreeDataProvider extends DisposableObject implements TreeDat
this._sortOrder = newSortOrder;
this._onDidChangeTreeData.fire(undefined);
}
private getItemDate(item: QueryHistoryInfo) {
switch (item.t) {
case 'local':
return item.initialInfo.start.getTime();
case 'remote':
return item.remoteQuery.executionStartTime;
case 'variant-analysis':
return item.variantAnalysis.executionStartTime;
default:
assertNever(item);
}
}
}
export class QueryHistoryManager extends DisposableObject {
@@ -649,10 +658,20 @@ export class QueryHistoryManager extends DisposableObject {
return;
}
const queryPath = finalSingleItem.t === 'local'
? finalSingleItem.initialInfo.queryPath
: finalSingleItem.remoteQuery.queryFilePath;
let queryPath: string;
switch (finalSingleItem.t) {
case 'local':
queryPath = finalSingleItem.initialInfo.queryPath;
break;
case 'remote':
queryPath = finalSingleItem.remoteQuery.queryFilePath;
break;
case 'variant-analysis':
queryPath = finalSingleItem.variantAnalysis.query.filePath;
break;
default:
assertNever(finalSingleItem);
}
const textDocument = await workspace.openTextDocument(
Uri.file(queryPath)
);
@@ -710,8 +729,12 @@ export class QueryHistoryManager extends DisposableObject {
// We need to delete it from disk as well.
await item.completedQuery?.query.deleteQuery();
}
} else {
} else if (item.t === 'remote') {
await this.removeRemoteQuery(item);
} else if (item.t === 'variant-analysis') {
// TODO
} else {
assertNever(item);
}
}));
@@ -1025,15 +1048,20 @@ export class QueryHistoryManager extends DisposableObject {
isQuickEval: String(!!(finalSingleItem.t === 'local' && finalSingleItem.initialInfo.quickEvalPosition)),
queryText: encodeURIComponent(await this.getQueryText(finalSingleItem)),
});
const queryId = finalSingleItem.t === 'local'
? finalSingleItem.initialInfo.id
: finalSingleItem.queryId;
const uri = Uri.parse(
`codeql:${queryId}?${params.toString()}`, true
);
const doc = await workspace.openTextDocument(uri);
await window.showTextDocument(doc, { preview: false });
if (finalSingleItem.t === 'variant-analysis') {
// TODO
} else {
const queryId = finalSingleItem.t === 'local'
? finalSingleItem.initialInfo.id
: finalSingleItem.queryId;
const uri = Uri.parse(
`codeql:${queryId}?${params.toString()}`, true
);
const doc = await workspace.openTextDocument(uri);
await window.showTextDocument(doc, { preview: false });
}
}
async handleViewSarifAlerts(
@@ -1149,9 +1177,16 @@ export class QueryHistoryManager extends DisposableObject {
}
async getQueryText(item: QueryHistoryInfo): Promise<string> {
return item.t === 'local'
? item.initialInfo.queryText
: item.remoteQuery.queryText;
switch (item.t) {
case 'local':
return item.initialInfo.queryText;
case 'remote':
return item.remoteQuery.queryText;
case 'variant-analysis':
return 'TODO';
default:
assertNever(item);
}
}
async handleExportResults(): Promise<void> {

View File

@@ -19,6 +19,7 @@ import { QueryStatus } from './query-status';
import { RemoteQueryHistoryItem } from './remote-queries/remote-query-history-item';
import { QueryEvaluationInfo, QueryWithResults } from './run-queries-shared';
import { formatLegacyMessage } from './legacy-query-server/run-queries';
import { VariantAnalysisHistoryItem } from './remote-queries/variant-analysis-history-item';
/**
* query-results.ts
@@ -201,13 +202,13 @@ export function ensureMetadataIsComplete(metadata: QueryMetadata | undefined) {
}
/**
* Used in Interface and Compare-Interface for queries that we know have been complated.
* Used in Interface and Compare-Interface for queries that we know have been completed.
*/
export type CompletedLocalQueryInfo = LocalQueryInfo & {
completedQuery: CompletedQueryInfo
};
export type QueryHistoryInfo = LocalQueryInfo | RemoteQueryHistoryItem;
export type QueryHistoryInfo = LocalQueryInfo | RemoteQueryHistoryItem | VariantAnalysisHistoryItem;
export class LocalQueryInfo {
readonly t = 'local';

View File

@@ -15,8 +15,8 @@ export async function slurpQueryHistory(fsPath: string): Promise<QueryHistoryInf
const data = await fs.readFile(fsPath, 'utf8');
const obj = JSON.parse(data);
if (obj.version !== 1) {
void showAndLogErrorMessage(`Unsupported query history format: v${obj.version}. `);
if (![1, 2].includes(obj.version)) {
void showAndLogErrorMessage(`Can't parse query history. Unsupported query history format: v${obj.version}. `);
return [];
}
@@ -54,7 +54,7 @@ export async function slurpQueryHistory(fsPath: string): Promise<QueryHistoryInf
// most likely another workspace has deleted them because the
// queries aged out.
return asyncFilter(parsedQueries, async (q) => {
if (q.t === 'remote') {
if (q.t === 'remote' || q.t === 'variant-analysis') {
// the slurper doesn't know where the remote queries are stored
// so we need to assume here that they exist. Later, we check to
// see if they exist on disk.
@@ -90,7 +90,7 @@ export async function splatQueryHistory(queries: QueryHistoryInfo[], fsPath: str
// remove incomplete local queries since they cannot be recreated on restart
const filteredQueries = queries.filter(q => q.t === 'local' ? q.completedQuery !== undefined : true);
const data = JSON.stringify({
version: 1,
version: 2, // version 2 adds the `variant-analysis` type.
queries: filteredQueries
}, null, 2);
await fs.writeFile(fsPath, data);

View File

@@ -0,0 +1,15 @@
import { QueryStatus } from '../query-status';
import { VariantAnalysis } from './shared/variant-analysis';
/**
* Information about a variant analysis.
*/
export interface VariantAnalysisHistoryItem {
readonly t: 'variant-analysis';
failureReason?: string;
resultCount?: number;
status: QueryStatus;
completed: boolean;
variantAnalysis: VariantAnalysis;
userSpecifiedLabel?: string;
}

View File

@@ -290,7 +290,7 @@ describe('query-results', () => {
it('should handle an invalid query history version', async () => {
const badPath = path.join(tmpDir.name, 'bad-query-history.json');
fs.writeFileSync(badPath, JSON.stringify({
version: 2,
version: 3,
queries: allHistory
}), 'utf8');

View File

@@ -164,7 +164,7 @@ describe('Remote queries and query history manager', function() {
// also, both queries should be removed from on disk storage
expect(fs.readJSONSync(path.join(STORAGE_DIR, 'workspace-query-history.json'))).to.deep.eq({
version: 1,
version: 2,
queries: []
});
});