Query history: Add new VariantAnalysisHistoryItem type (#1590)
This commit is contained in:
@@ -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),
|
||||
'%': '%',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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: []
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user