Store query output dir on history items

This will add the `QueryOutputDir` to the `InitialQueryInfo` and
populate it when creating a local query history item. This will allow us
to open the results directory or show the evaluator log without a
completed query.
This commit is contained in:
Koen Vlaswinkel
2023-10-18 16:03:09 +02:00
parent df55e039a1
commit 9928c338e9
10 changed files with 101 additions and 14 deletions

View File

@@ -362,11 +362,15 @@ export class LocalQueries extends DisposableObject {
);
}
const initialInfo = await createInitialQueryInfo(selectedQuery, {
databaseUri: dbItem.databaseUri.toString(),
name: dbItem.name,
language: tryGetQueryLanguage(dbItem.language),
});
const initialInfo = await createInitialQueryInfo(
selectedQuery,
{
databaseUri: dbItem.databaseUri.toString(),
name: dbItem.name,
language: tryGetQueryLanguage(dbItem.language),
},
outputDir,
);
// When cancellation is requested from the query history view, we just stop the debug session.
const queryInfo = new LocalQueryInfo(initialInfo, tokenSource);

View File

@@ -723,11 +723,12 @@ export class QueryHistoryManager extends DisposableObject {
async handleOpenQueryDirectory(item: QueryHistoryInfo) {
let externalFilePath: string | undefined;
if (item.t === "local") {
if (item.completedQuery) {
externalFilePath = join(
item.completedQuery.query.querySaveDir,
"timestamp",
);
const querySaveDir =
item.initialInfo.outputDir?.querySaveDir ??
item.completedQuery?.query.querySaveDir;
if (querySaveDir) {
externalFilePath = join(querySaveDir, "timestamp");
}
} else if (item.t === "variant-analysis") {
externalFilePath = join(
@@ -761,9 +762,18 @@ export class QueryHistoryManager extends DisposableObject {
`Failed to open ${externalFilePath}: ${getErrorMessage(e)}`,
);
}
} else {
this.warnNoQueryDir();
}
}
private warnNoQueryDir() {
void showAndLogWarningMessage(
this.app.logger,
`Results directory is not available for this run.`,
);
}
private warnNoEvalLogs() {
void showAndLogWarningMessage(
this.app.logger,

View File

@@ -109,6 +109,11 @@ function mapInitialQueryInfoToDto(
},
start: localQueryInitialInfo.start,
id: localQueryInitialInfo.id,
outputDir: localQueryInitialInfo.outputDir
? {
querySaveDir: localQueryInitialInfo.outputDir.querySaveDir,
}
: undefined,
};
}

View File

@@ -3,7 +3,7 @@ import {
CompletedQueryInfo,
InitialQueryInfo,
} from "../../query-results";
import { QueryEvaluationInfo } from "../../run-queries-shared";
import { QueryEvaluationInfo, QueryOutputDir } from "../../run-queries-shared";
import {
CompletedQueryInfoDto,
QueryEvaluationInfoDto,
@@ -26,7 +26,10 @@ export function mapLocalQueryItemToDomainModel(
localQuery: QueryHistoryLocalQueryDto,
): LocalQueryInfo {
return new LocalQueryInfo(
mapInitialQueryInfoToDomainModel(localQuery.initialInfo),
mapInitialQueryInfoToDomainModel(
localQuery.initialInfo,
localQuery.completedQuery?.query?.querySaveDir,
),
undefined,
localQuery.failureReason,
localQuery.completedQuery &&
@@ -72,7 +75,14 @@ function mapCompletedQueryInfoToDomainModel(
function mapInitialQueryInfoToDomainModel(
initialInfo: InitialQueryInfoDto,
// The completedQuerySaveDir is a migration to support old query items that don't have
// the querySaveDir in the initialInfo. It should be removed once all query
// items have the querySaveDir in the initialInfo.
completedQuerySaveDir?: string,
): InitialQueryInfo {
const querySaveDir =
initialInfo.outputDir?.querySaveDir ?? completedQuerySaveDir;
return {
userSpecifiedLabel: initialInfo.userSpecifiedLabel,
queryText: initialInfo.queryText,
@@ -90,6 +100,7 @@ function mapInitialQueryInfoToDomainModel(
},
start: new Date(initialInfo.start),
id: initialInfo.id,
outputDir: querySaveDir ? new QueryOutputDir(querySaveDir) : undefined,
};
}

View File

@@ -24,6 +24,11 @@ export interface InitialQueryInfoDto {
databaseInfo: DatabaseInfoDto;
start: Date;
id: string;
outputDir?: QueryOutputDirDto; // Undefined for backwards compatibility
}
interface QueryOutputDirDto {
querySaveDir: string;
}
interface DatabaseInfoDto {

View File

@@ -19,6 +19,7 @@ import { QueryStatus } from "./query-history/query-status";
import {
EvaluatorLogPaths,
QueryEvaluationInfo,
QueryOutputDir,
QueryWithResults,
} from "./run-queries-shared";
import { formatLegacyMessage } from "./query-server/legacy";
@@ -47,6 +48,7 @@ export interface InitialQueryInfo {
readonly databaseInfo: DatabaseInfo;
readonly start: Date;
readonly id: string; // unique id for this query.
readonly outputDir?: QueryOutputDir; // If missing, we do not have a query save dir. The query may have been cancelled. This is only for backwards compatibility.
}
export class CompletedQueryInfo implements QueryWithResults {

View File

@@ -562,11 +562,13 @@ async function convertToQlPath(filePath: string): Promise<string> {
*
* @param selectedQuery The query to run, including any quickeval info.
* @param databaseInfo The database to run the query against.
* @param outputDir The output directory for this query.
* @returns The initial information for the query to be run.
*/
export async function createInitialQueryInfo(
selectedQuery: SelectedQuery,
databaseInfo: DatabaseInfo,
outputDir: QueryOutputDir,
): Promise<InitialQueryInfo> {
const isQuickEval = selectedQuery.quickEval !== undefined;
const isQuickEvalCount =
@@ -587,6 +589,7 @@ export async function createInitialQueryInfo(
: {
queryText: await readFile(selectedQuery.queryPath, "utf8"),
}),
outputDir,
};
}

View File

@@ -2,6 +2,7 @@ import { faker } from "@faker-js/faker";
import { InitialQueryInfo, LocalQueryInfo } from "../../../src/query-results";
import {
QueryEvaluationInfo,
QueryOutputDir,
QueryWithResults,
} from "../../../src/run-queries-shared";
import { CancellationTokenSource } from "vscode";
@@ -18,6 +19,7 @@ export function createMockLocalQueryInfo({
hasMetadata = false,
queryWithResults = undefined,
language = undefined,
outputDir = new QueryOutputDir("/a/b/c"),
}: {
startTime?: Date;
resultCount?: number;
@@ -27,6 +29,7 @@ export function createMockLocalQueryInfo({
hasMetadata?: boolean;
queryWithResults?: QueryWithResults | undefined;
language?: QueryLanguage;
outputDir?: QueryOutputDir | undefined;
}): LocalQueryInfo {
const cancellationToken = {
dispose: () => {
@@ -48,6 +51,7 @@ export function createMockLocalQueryInfo({
start: startTime,
id: faker.number.int().toString(),
userSpecifiedLabel,
outputDir,
} as InitialQueryInfo;
const localQuery = new LocalQueryInfo(initialQueryInfo, cancellationToken);

View File

@@ -8,7 +8,10 @@ import {
LocalQueryInfo,
InitialQueryInfo,
} from "../../../../../src/query-results";
import { QueryWithResults } from "../../../../../src/run-queries-shared";
import {
QueryOutputDir,
QueryWithResults,
} from "../../../../../src/run-queries-shared";
import { DatabaseInfo } from "../../../../../src/common/interface-types";
import { CancellationTokenSource, Uri } from "vscode";
import { tmpDir } from "../../../../../src/tmp-dir";
@@ -130,6 +133,38 @@ describe("write and read", () => {
expect(allHistoryActual.length).toEqual(expectedHistory.length);
});
it("should read query output dir from completed query if not present", async () => {
const historyPath = join(tmpDir.name, "workspace-query-history.json");
const queryItem = createMockFullQueryInfo(
"a",
createMockQueryWithResults(
`${queryPath}-a`,
false,
false,
"/a/b/c/a",
false,
),
false,
null,
);
// write and read
await writeQueryHistoryToFile([queryItem], historyPath);
const actual = await readQueryHistoryFromFile(historyPath);
expect(actual).toHaveLength(1);
expect(actual[0].t).toEqual("local");
if (actual[0].t === "local") {
expect(actual[0].initialInfo.outputDir?.querySaveDir).not.toBeUndefined();
expect(actual[0].initialInfo.outputDir?.querySaveDir).toEqual(
queryItem.completedQuery?.query?.querySaveDir,
);
}
});
it("should remove remote queries from the history", async () => {
const path = join(tmpDir.name, "query-history-with-remote.json");
await writeFile(
@@ -205,6 +240,9 @@ describe("write and read", () => {
dbName = "a",
queryWithResults?: QueryWithResults,
isFail = false,
outputDir: QueryOutputDir | null = new QueryOutputDir(
"/path/to/output/dir",
),
): LocalQueryInfo {
const fqi = new LocalQueryInfo(
{
@@ -218,6 +256,7 @@ describe("write and read", () => {
isQuickQuery: false,
isQuickEval: false,
id: `some-id-${dbName}`,
outputDir: outputDir ? outputDir : undefined,
} as InitialQueryInfo,
{
dispose: () => {

View File

@@ -12,7 +12,10 @@ import {
InitialQueryInfo,
interpretResultsSarif,
} from "../../../src/query-results";
import { QueryWithResults } from "../../../src/run-queries-shared";
import {
QueryOutputDir,
QueryWithResults,
} from "../../../src/run-queries-shared";
import {
DatabaseInfo,
SortDirection,
@@ -506,6 +509,7 @@ describe("query-results", () => {
isQuickQuery: false,
isQuickEval: false,
id: `some-id-${dbName}`,
outputDir: new QueryOutputDir("path/to/output/dir"),
} as InitialQueryInfo,
{
dispose: () => {