Merge branch 'main' into github-action/bump-cli

This commit is contained in:
Shati Patel
2023-04-20 10:30:07 +01:00
committed by GitHub
62 changed files with 1340 additions and 809 deletions

View File

@@ -2,7 +2,9 @@
## [UNRELEASED]
- Added ability to filter repositories for a variant analysis to only those that have results [#2343](https://github.com/github/vscode-codeql/pull/2343)
- Add new configuration option to allow downloading databases from http, non-secure servers. [#2332](https://github.com/github/vscode-codeql/pull/2332)
- Remove title actions from the query history panel that depended on history items being selected. [#2350](https://github.com/github/vscode-codeql/pull/2350)
## 1.8.2 - 12 April 2023

View File

@@ -340,6 +340,12 @@
"type": "boolean",
"default": false,
"description": "Allow database to be downloaded via HTTP. Warning: enabling this option will allow downloading from insecure servers."
},
"codeQL.createQuery.folder": {
"type": "string",
"default": "",
"patternErrorMessage": "Please enter a valid folder",
"markdownDescription": "The name of the folder where we want to create queries and query packs via the \"CodeQL: Create Query\" command. The folder should exist."
}
}
},
@@ -627,11 +633,6 @@
"command": "codeQL.checkForUpdatesToCLI",
"title": "CodeQL: Check for CLI Updates"
},
{
"command": "codeQLQueryHistory.openQueryTitleMenu",
"title": "View Query",
"icon": "$(edit)"
},
{
"command": "codeQLQueryHistory.openQueryContextMenu",
"title": "View Query",
@@ -642,11 +643,6 @@
"title": "Open Query Results",
"icon": "$(preview)"
},
{
"command": "codeQLQueryHistory.removeHistoryItemTitleMenu",
"title": "Delete",
"icon": "$(trash)"
},
{
"command": "codeQLQueryHistory.removeHistoryItemContextMenu",
"title": "Delete",
@@ -847,21 +843,6 @@
"when": "view == codeQLDatabases",
"group": "navigation"
},
{
"command": "codeQLQueryHistory.openQueryTitleMenu",
"when": "view == codeQLQueryHistory",
"group": "navigation"
},
{
"command": "codeQLQueryHistory.itemClicked",
"when": "view == codeQLQueryHistory",
"group": "navigation"
},
{
"command": "codeQLQueryHistory.removeHistoryItemTitleMenu",
"when": "view == codeQLQueryHistory",
"group": "navigation"
},
{
"command": "codeQLQueryHistory.sortByName",
"when": "view == codeQLQueryHistory",
@@ -1304,18 +1285,10 @@
"command": "codeQLDatabases.upgradeDatabase",
"when": "false"
},
{
"command": "codeQLQueryHistory.openQueryTitleMenu",
"when": "false"
},
{
"command": "codeQLQueryHistory.openQueryContextMenu",
"when": "false"
},
{
"command": "codeQLQueryHistory.removeHistoryItemTitleMenu",
"when": "false"
},
{
"command": "codeQLQueryHistory.removeHistoryItemContextMenu",
"when": "false"
@@ -1446,7 +1419,7 @@
},
{
"command": "codeQL.createQuery",
"when": "config.codeQL.canary"
"when": "config.codeQL.codespacesTemplate"
},
{
"command": "codeQLTests.acceptOutputContextTestItem",

View File

@@ -13,21 +13,6 @@ import type {
} from "../variant-analysis/shared/variant-analysis";
import type { QLDebugConfiguration } from "../debugger/debug-configuration";
// A command function matching the signature that VS Code calls when
// a command is invoked from the title bar of a TreeView with
// canSelectMany set to true.
//
// It is possible to get any combination of singleItem and multiSelect
// to be undefined. This is because it is possible to click a title bar
// option without interacting with any individual items first, or even
// when there are no items present at all.
// If both singleItem and multiSelect are defined, then singleItem will
// be contained within multiSelect.
export type TreeViewTitleMultiSelectionCommandFunction<Item> = (
singleItem: Item | undefined,
multiSelect: Item[] | undefined,
) => Promise<void>;
// A command function matching the signature that VS Code calls when
// a command is invoked from a context menu on a TreeView with
// canSelectMany set to true.
@@ -176,9 +161,7 @@ export type QueryHistoryCommands = {
"codeQLQueryHistory.sortByCount": () => Promise<void>;
// Commands in the context menu or in the hover menu
"codeQLQueryHistory.openQueryTitleMenu": TreeViewTitleMultiSelectionCommandFunction<QueryHistoryInfo>;
"codeQLQueryHistory.openQueryContextMenu": TreeViewContextMultiSelectionCommandFunction<QueryHistoryInfo>;
"codeQLQueryHistory.removeHistoryItemTitleMenu": TreeViewTitleMultiSelectionCommandFunction<QueryHistoryInfo>;
"codeQLQueryHistory.removeHistoryItemContextMenu": TreeViewContextMultiSelectionCommandFunction<QueryHistoryInfo>;
"codeQLQueryHistory.removeHistoryItemContextInline": TreeViewContextMultiSelectionCommandFunction<QueryHistoryInfo>;
"codeQLQueryHistory.renameItem": TreeViewContextMultiSelectionCommandFunction<QueryHistoryInfo>;
@@ -195,7 +178,7 @@ export type QueryHistoryCommands = {
"codeQLQueryHistory.viewCsvAlerts": TreeViewContextMultiSelectionCommandFunction<QueryHistoryInfo>;
"codeQLQueryHistory.viewSarifAlerts": TreeViewContextMultiSelectionCommandFunction<QueryHistoryInfo>;
"codeQLQueryHistory.viewDil": TreeViewContextMultiSelectionCommandFunction<QueryHistoryInfo>;
"codeQLQueryHistory.itemClicked": TreeViewTitleMultiSelectionCommandFunction<QueryHistoryInfo>;
"codeQLQueryHistory.itemClicked": TreeViewContextMultiSelectionCommandFunction<QueryHistoryInfo>;
"codeQLQueryHistory.openOnGithub": TreeViewContextMultiSelectionCommandFunction<QueryHistoryInfo>;
"codeQLQueryHistory.copyRepoList": TreeViewContextMultiSelectionCommandFunction<QueryHistoryInfo>;

View File

@@ -619,3 +619,19 @@ export const ALLOW_HTTP_SETTING = new Setting(
export function allowHttp(): boolean {
return ALLOW_HTTP_SETTING.getValue<boolean>() || false;
}
/**
* The name of the folder where we want to create skeleton wizard QL packs.
**/
const SKELETON_WIZARD_FOLDER = new Setting(
"folder",
new Setting("createQuery", ROOT_SETTING),
);
export function getSkeletonWizardFolder(): string | undefined {
return SKELETON_WIZARD_FOLDER.getValue<string>() || undefined;
}
export async function setSkeletonWizardFolder(folder: string | undefined) {
await SKELETON_WIZARD_FOLDER.updateValue(folder, ConfigurationTarget.Global);
}

View File

@@ -20,7 +20,7 @@ import {
} from "./queryResolver";
import { CancellationToken, LocationLink, Uri } from "vscode";
import { QueryOutputDir } from "../run-queries-shared";
import { QueryRunner } from "../queryRunner";
import { QueryRunner } from "../query-server";
import { QueryResultType } from "../pure/new-messages";
export const SELECT_QUERY_NAME = "#select";

View File

@@ -16,7 +16,7 @@ import { DatabaseItem } from "../local-databases";
import { extLogger, TeeLogger } from "../common";
import { CancellationToken } from "vscode";
import { ProgressCallback } from "../progress";
import { CoreCompletedQuery, QueryRunner } from "../queryRunner";
import { CoreCompletedQuery, QueryRunner } from "../query-server";
import { redactableError } from "../pure/errors";
import { QLPACK_FILENAMES } from "../pure/ql";

View File

@@ -32,7 +32,7 @@ import {
runContextualQuery,
} from "./queryResolver";
import { isCanary, NO_CACHE_AST_VIEWER } from "../config";
import { CoreCompletedQuery, QueryRunner } from "../queryRunner";
import { CoreCompletedQuery, QueryRunner } from "../query-server";
/**
* Runs templated CodeQL queries to find definitions in

View File

@@ -2,7 +2,7 @@ import { ExtensionContext } from "vscode";
import { DataExtensionsEditorView } from "./data-extensions-editor-view";
import { DataExtensionsEditorCommands } from "../common/commands";
import { CliVersionConstraint, CodeQLCliServer } from "../cli";
import { QueryRunner } from "../queryRunner";
import { QueryRunner } from "../query-server";
import { DatabaseManager } from "../local-databases";
import { ensureDir } from "fs-extra";
import { join } from "path";

View File

@@ -13,7 +13,7 @@ import {
ToDataExtensionsEditorMessage,
} from "../pure/interface-types";
import { ProgressUpdate } from "../progress";
import { QueryRunner } from "../queryRunner";
import { QueryRunner } from "../query-server";
import {
showAndLogErrorMessage,
showAndLogExceptionWithTelemetry,
@@ -35,7 +35,7 @@ import { readQueryResults, runQuery } from "./external-api-usage-query";
import { createDataExtensionYaml, loadDataExtensionYaml } from "./yaml";
import { ExternalApiUsage } from "./external-api-usage";
import { ModeledMethod } from "./modeled-method";
import { ExtensionPackModelFile } from "./extension-pack-picker";
import { ExtensionPackModelFile } from "./shared/extension-pack";
function getQlSubmoduleFolder(): WorkspaceFolder | undefined {
const workspaceFolder = workspace.workspaceFolders?.find(
@@ -118,7 +118,7 @@ export class DataExtensionsEditorView extends AbstractWebview<
msg.externalApiUsages,
msg.modeledMethods,
);
await this.loadExternalApiUsages();
await Promise.all([this.setViewState(), this.loadExternalApiUsages()]);
break;
case "generateExternalApi":
@@ -134,16 +134,22 @@ export class DataExtensionsEditorView extends AbstractWebview<
super.onWebViewLoaded();
await Promise.all([
this.postMessage({
t: "setDataExtensionEditorInitialData",
extensionPackName: this.modelFile.extensionPack.name,
modelFilename: this.modelFile.filename,
}),
this.setViewState(),
this.loadExternalApiUsages(),
this.loadExistingModeledMethods(),
]);
}
private async setViewState(): Promise<void> {
await this.postMessage({
t: "setDataExtensionEditorViewState",
viewState: {
extensionPackModelFile: this.modelFile,
modelFileExists: await pathExists(this.modelFile.filename),
},
});
}
protected async jumpToUsage(
location: ResolvableLocationValue,
): Promise<void> {

View File

@@ -13,6 +13,7 @@ import { ProgressCallback } from "../progress";
import { DatabaseItem } from "../local-databases";
import { getQlPackPath, QLPACK_FILENAMES } from "../pure/ql";
import { getErrorMessage } from "../pure/helpers-pure";
import { ExtensionPack, ExtensionPackModelFile } from "./shared/extension-pack";
const maxStep = 3;
@@ -22,22 +23,6 @@ const packNameRegex = new RegExp(
);
const packNameLength = 128;
export interface ExtensionPack {
path: string;
yamlPath: string;
name: string;
version: string;
extensionTargets: Record<string, string>;
dataExtensions: string[];
}
export interface ExtensionPackModelFile {
filename: string;
extensionPack: ExtensionPack;
}
export async function pickExtensionPackModelFile(
cliServer: Pick<CodeQLCliServer, "resolveQlpacks" | "resolveExtensions">,
databaseItem: Pick<DatabaseItem, "name" | "language">,

View File

@@ -1,4 +1,4 @@
import { CoreCompletedQuery, QueryRunner } from "../queryRunner";
import { CoreCompletedQuery, QueryRunner } from "../query-server";
import { dir } from "tmp-promise";
import { writeFile } from "fs-extra";
import { dump as dumpYaml } from "js-yaml";

View File

@@ -1,7 +1,7 @@
import { CancellationToken } from "vscode";
import { DatabaseItem } from "../local-databases";
import { join } from "path";
import { QueryRunner } from "../queryRunner";
import { QueryRunner } from "../query-server";
import { CodeQLCliServer } from "../cli";
import { TeeLogger } from "../common";
import { extensiblePredicateDefinitions } from "./predicates";

View File

@@ -0,0 +1,15 @@
export interface ExtensionPack {
path: string;
yamlPath: string;
name: string;
version: string;
extensionTargets: Record<string, string>;
dataExtensions: string[];
}
export interface ExtensionPackModelFile {
filename: string;
extensionPack: ExtensionPack;
}

View File

@@ -0,0 +1,6 @@
import { ExtensionPackModelFile } from "./extension-pack";
export interface DataExtensionEditorViewState {
extensionPackModelFile: ExtensionPackModelFile;
modelFileExists: boolean;
}

View File

@@ -14,7 +14,7 @@ import { Disposable } from "vscode";
import { CancellationTokenSource } from "vscode-jsonrpc";
import { BaseLogger, LogOptions, queryServerLogger } from "../common";
import { QueryResultType } from "../pure/new-messages";
import { CoreQueryResults, CoreQueryRun, QueryRunner } from "../queryRunner";
import { CoreQueryResults, CoreQueryRun, QueryRunner } from "../query-server";
import * as CodeQLProtocol from "./debug-protocol";
import { QuickEvalContext } from "../run-queries-shared";
import { getErrorMessage } from "../pure/helpers-pure";

View File

@@ -11,7 +11,7 @@ import {
import { isCanary } from "../config";
import { LocalQueries } from "../local-queries";
import { DisposableObject } from "../pure/disposable-object";
import { QueryRunner } from "../queryRunner";
import { QueryRunner } from "../query-server";
import { QLDebugConfigurationProvider } from "./debug-configuration";
import { QLDebugSession } from "./debug-session";

View File

@@ -9,9 +9,8 @@ import {
} from "vscode";
import { DebuggerCommands } from "../common/commands";
import { DatabaseManager } from "../local-databases";
import { LocalQueries, LocalQueryRun } from "../local-queries";
import { DisposableObject } from "../pure/disposable-object";
import { CoreQueryResults } from "../queryRunner";
import { CoreQueryResults } from "../query-server";
import {
getQuickEvalContext,
QueryOutputDir,
@@ -20,6 +19,7 @@ import {
import { QLResolvedDebugConfiguration } from "./debug-configuration";
import * as CodeQLProtocol from "./debug-protocol";
import { App } from "../common/app";
import { LocalQueryRun, LocalQueries } from "../local-queries";
/**
* Listens to messages passing between VS Code and the debug adapter, so that we can supplement the

View File

@@ -83,8 +83,10 @@ import {
} from "./common";
import { QueryHistoryManager } from "./query-history/query-history-manager";
import { CompletedLocalQueryInfo } from "./query-results";
import { QueryServerClient as LegacyQueryServerClient } from "./legacy-query-server/queryserver-client";
import { QueryServerClient } from "./query-server/queryserver-client";
import {
LegacyQueryRunner,
QueryServerClient as LegacyQueryServerClient,
} from "./query-server/legacy";
import { QLTestAdapterFactory } from "./test-adapter";
import { TestUIService } from "./test-ui";
import { CompareView } from "./compare/compare-view";
@@ -97,9 +99,6 @@ import { EvalLogViewer } from "./eval-log-viewer";
import { SummaryLanguageSupport } from "./log-insights/summary-language-support";
import { JoinOrderScannerProvider } from "./log-insights/join-order";
import { LogScannerService } from "./log-insights/log-scanner-service";
import { LegacyQueryRunner } from "./legacy-query-server/legacyRunner";
import { NewQueryRunner } from "./query-server/query-runner";
import { QueryRunner } from "./queryRunner";
import { VariantAnalysisView } from "./variant-analysis/variant-analysis-view";
import { VariantAnalysisViewSerializer } from "./variant-analysis/variant-analysis-view-serializer";
import { VariantAnalysisManager } from "./variant-analysis/variant-analysis-manager";
@@ -127,6 +126,7 @@ import { DataExtensionsEditorModule } from "./data-extensions-editor/data-extens
import { TestManager } from "./test-manager";
import { TestRunner } from "./test-runner";
import { TestManagerBase } from "./test-manager-base";
import { NewQueryRunner, QueryRunner, QueryServerClient } from "./query-server";
/**
* extension.ts
@@ -707,9 +707,14 @@ async function activateWithInstalledDistribution(
for (const glob of PACK_GLOBS) {
const fsWatcher = workspace.createFileSystemWatcher(glob);
ctx.subscriptions.push(fsWatcher);
fsWatcher.onDidChange(async (_uri) => {
const clearPackCache = async (_uri: Uri) => {
await qs.clearPackCache();
});
};
fsWatcher.onDidCreate(clearPackCache);
fsWatcher.onDidChange(clearPackCache);
fsWatcher.onDidDelete(clearPackCache);
}
void extLogger.log("Initializing database manager.");

View File

@@ -41,7 +41,7 @@ import {
promptImportInternetDatabase,
} from "./databaseFetcher";
import { asError, asyncFilter, getErrorMessage } from "./pure/helpers-pure";
import { QueryRunner } from "./queryRunner";
import { QueryRunner } from "./query-server";
import { isCanary } from "./config";
import { App } from "./common/app";
import { redactableError } from "./pure/errors";

View File

@@ -23,7 +23,7 @@ import {
import { DisposableObject } from "./pure/disposable-object";
import { Logger, extLogger } from "./common";
import { asError, getErrorMessage } from "./pure/helpers-pure";
import { QueryRunner } from "./queryRunner";
import { QueryRunner } from "./query-server";
import { pathsEqual } from "./pure/files";
import { redactableError } from "./pure/errors";
import { isCodespacesTemplate } from "./config";

View File

@@ -0,0 +1,2 @@
export * from "./local-queries";
export * from "./local-query-run";

View File

@@ -1,4 +1,4 @@
import { ProgressCallback, ProgressUpdate, withProgress } from "./progress";
import { ProgressCallback, ProgressUpdate, withProgress } from "../progress";
import {
CancellationToken,
CancellationTokenSource,
@@ -8,76 +8,46 @@ import {
window,
workspace,
} from "vscode";
import { BaseLogger, extLogger, Logger, TeeLogger } from "./common";
import { isCanary, MAX_QUERIES } from "./config";
import { gatherQlFiles } from "./pure/files";
import { extLogger, TeeLogger } from "../common";
import { isCanary, MAX_QUERIES } from "../config";
import { gatherQlFiles } from "../pure/files";
import { basename } from "path";
import {
createTimestampFile,
findLanguage,
getOnDiskWorkspaceFolders,
showAndLogErrorMessage,
showAndLogExceptionWithTelemetry,
showAndLogWarningMessage,
showBinaryChoiceDialog,
tryGetQueryMetadata,
} from "./helpers";
import { displayQuickQuery } from "./quick-query";
import {
CoreCompletedQuery,
CoreQueryResults,
QueryRunner,
} from "./queryRunner";
import { QueryHistoryManager } from "./query-history/query-history-manager";
import { DatabaseUI } from "./local-databases-ui";
import { ResultsView } from "./interface";
import { DatabaseItem, DatabaseManager } from "./local-databases";
} from "../helpers";
import { displayQuickQuery } from "../quick-query";
import { CoreCompletedQuery, QueryRunner } from "../query-server";
import { QueryHistoryManager } from "../query-history/query-history-manager";
import { DatabaseUI } from "../local-databases-ui";
import { ResultsView } from "../interface";
import { DatabaseItem, DatabaseManager } from "../local-databases";
import {
createInitialQueryInfo,
EvaluatorLogPaths,
generateEvalLogSummaries,
getQuickEvalContext,
logEndSummary,
promptUserToSaveChanges,
QueryEvaluationInfo,
QueryOutputDir,
QueryWithResults,
SelectedQuery,
validateQueryUri,
} from "./run-queries-shared";
import { CompletedLocalQueryInfo, LocalQueryInfo } from "./query-results";
import { WebviewReveal } from "./interface-utils";
import { asError, getErrorMessage } from "./pure/helpers-pure";
import { CodeQLCliServer } from "./cli";
import { LocalQueryCommands } from "./common/commands";
import { App } from "./common/app";
import { DisposableObject } from "./pure/disposable-object";
import { QueryResultType } from "./pure/new-messages";
import { redactableError } from "./pure/errors";
import { SkeletonQueryWizard } from "./skeleton-query-wizard";
} from "../run-queries-shared";
import { CompletedLocalQueryInfo, LocalQueryInfo } from "../query-results";
import { WebviewReveal } from "../interface-utils";
import { asError, getErrorMessage } from "../pure/helpers-pure";
import { CodeQLCliServer } from "../cli";
import { LocalQueryCommands } from "../common/commands";
import { App } from "../common/app";
import { DisposableObject } from "../pure/disposable-object";
import { SkeletonQueryWizard } from "../skeleton-query-wizard";
import { LocalQueryRun } from "./local-query-run";
interface DatabaseQuickPickItem extends QuickPickItem {
databaseItem: DatabaseItem;
}
function formatResultMessage(result: CoreQueryResults): string {
switch (result.resultType) {
case QueryResultType.CANCELLATION:
return `cancelled after ${Math.round(
result.evaluationTime / 1000,
)} seconds`;
case QueryResultType.OOM:
return "out of memory";
case QueryResultType.SUCCESS:
return `finished in ${Math.round(result.evaluationTime / 1000)} seconds`;
case QueryResultType.COMPILATION_ERROR:
return `compilation failed: ${result.message}`;
case QueryResultType.OTHER_ERROR:
default:
return result.message ? `failed: ${result.message}` : "failed";
}
}
/**
* If either the query file or the quickeval file is dirty, give the user the chance to save them.
*/
@@ -97,142 +67,6 @@ async function promptToSaveQueryIfNeeded(query: SelectedQuery): Promise<void> {
}
}
/**
* Tracks the evaluation of a local query, including its interactions with the UI.
*
* The client creates an instance of `LocalQueryRun` when the evaluation starts, and then invokes
* the `complete()` function once the query has completed (successfully or otherwise).
*
* Having the client tell the `LocalQueryRun` when the evaluation is complete, rather than having
* the `LocalQueryRun` manage the evaluation itself, may seem a bit clunky. It's done this way
* because once we move query evaluation into a Debug Adapter, the debugging UI drives the
* evaluation, and we can only respond to events from the debug adapter.
*/
export class LocalQueryRun {
public constructor(
private readonly outputDir: QueryOutputDir,
private readonly localQueries: LocalQueries,
private readonly queryInfo: LocalQueryInfo,
private readonly dbItem: DatabaseItem,
public readonly logger: Logger, // Public so that other clients, like the debug adapter, know where to send log output
private readonly queryHistoryManager: QueryHistoryManager,
private readonly cliServer: CodeQLCliServer,
) {}
/**
* Updates the UI based on the results of the query evaluation. This creates the evaluator log
* summaries, updates the query history item for the evaluation with the results and evaluation
* time, and displays the results view.
*
* This function must be called when the evaluation completes, whether the evaluation was
* successful or not.
* */
public async complete(results: CoreQueryResults): Promise<void> {
const evalLogPaths = await this.summarizeEvalLog(
results.resultType,
this.outputDir,
this.logger,
);
if (evalLogPaths !== undefined) {
this.queryInfo.setEvaluatorLogPaths(evalLogPaths);
}
const queryWithResults = await this.getCompletedQueryInfo(results);
this.queryHistoryManager.completeQuery(this.queryInfo, queryWithResults);
await this.localQueries.showResultsForCompletedQuery(
this.queryInfo as CompletedLocalQueryInfo,
WebviewReveal.Forced,
);
// Note we must update the query history view after showing results as the
// display and sorting might depend on the number of results
await this.queryHistoryManager.refreshTreeView();
}
/**
* Updates the UI in the case where query evaluation throws an exception.
*/
public async fail(err: Error): Promise<void> {
err.message = `Error running query: ${err.message}`;
this.queryInfo.failureReason = err.message;
await this.queryHistoryManager.refreshTreeView();
}
/**
* Generate summaries of the structured evaluator log.
*/
private async summarizeEvalLog(
resultType: QueryResultType,
outputDir: QueryOutputDir,
logger: BaseLogger,
): Promise<EvaluatorLogPaths | undefined> {
const evalLogPaths = await generateEvalLogSummaries(
this.cliServer,
outputDir,
);
if (evalLogPaths !== undefined) {
if (evalLogPaths.endSummary !== undefined) {
void logEndSummary(evalLogPaths.endSummary, logger); // Logged asynchrnously
}
} else {
// Raw evaluator log was not found. Notify the user, unless we know why it wasn't found.
if (resultType === QueryResultType.SUCCESS) {
void showAndLogWarningMessage(
`Failed to write structured evaluator log to ${outputDir.evalLogPath}.`,
);
} else {
// Don't bother notifying the user if there's no log. For some errors, like compilation
// errors, we don't expect a log. For cancellations and OOM errors, whether or not we have
// a log depends on how far execution got before termination.
}
}
return evalLogPaths;
}
/**
* Gets a `QueryWithResults` containing information about the evaluation of the query and its
* result, in the form expected by the query history UI.
*/
private async getCompletedQueryInfo(
results: CoreQueryResults,
): Promise<QueryWithResults> {
// Read the query metadata if possible, to use in the UI.
const metadata = await tryGetQueryMetadata(
this.cliServer,
this.queryInfo.initialInfo.queryPath,
);
const query = new QueryEvaluationInfo(
this.outputDir.querySaveDir,
this.dbItem.databaseUri.fsPath,
await this.dbItem.hasMetadataFile(),
this.queryInfo.initialInfo.quickEvalPosition,
metadata,
);
if (results.resultType !== QueryResultType.SUCCESS) {
const message = results.message
? redactableError`Failed to run query: ${results.message}`
: redactableError`Failed to run query`;
void showAndLogExceptionWithTelemetry(message);
}
const message = formatResultMessage(results);
const successful = results.resultType === QueryResultType.SUCCESS;
return {
query,
result: {
evaluationTime: results.evaluationTime,
queryId: 0,
resultType: successful
? QueryResultType.SUCCESS
: QueryResultType.OTHER_ERROR,
runId: 0,
message,
},
message,
successful,
};
}
}
export class LocalQueries extends DisposableObject {
public constructor(
private readonly app: App,

View File

@@ -0,0 +1,177 @@
import { BaseLogger, Logger } from "../common";
import {
showAndLogExceptionWithTelemetry,
showAndLogWarningMessage,
tryGetQueryMetadata,
} from "../helpers";
import { CoreQueryResults } from "../query-server";
import { QueryHistoryManager } from "../query-history/query-history-manager";
import { DatabaseItem } from "../local-databases";
import {
EvaluatorLogPaths,
generateEvalLogSummaries,
logEndSummary,
QueryEvaluationInfo,
QueryOutputDir,
QueryWithResults,
} from "../run-queries-shared";
import { CompletedLocalQueryInfo, LocalQueryInfo } from "../query-results";
import { WebviewReveal } from "../interface-utils";
import { CodeQLCliServer } from "../cli";
import { QueryResultType } from "../pure/new-messages";
import { redactableError } from "../pure/errors";
import { LocalQueries } from "./local-queries";
function formatResultMessage(result: CoreQueryResults): string {
switch (result.resultType) {
case QueryResultType.CANCELLATION:
return `cancelled after ${Math.round(
result.evaluationTime / 1000,
)} seconds`;
case QueryResultType.OOM:
return "out of memory";
case QueryResultType.SUCCESS:
return `finished in ${Math.round(result.evaluationTime / 1000)} seconds`;
case QueryResultType.COMPILATION_ERROR:
return `compilation failed: ${result.message}`;
case QueryResultType.OTHER_ERROR:
default:
return result.message ? `failed: ${result.message}` : "failed";
}
}
/**
* Tracks the evaluation of a local query, including its interactions with the UI.
*
* The client creates an instance of `LocalQueryRun` when the evaluation starts, and then invokes
* the `complete()` function once the query has completed (successfully or otherwise).
*
* Having the client tell the `LocalQueryRun` when the evaluation is complete, rather than having
* the `LocalQueryRun` manage the evaluation itself, may seem a bit clunky. It's done this way
* because once we move query evaluation into a Debug Adapter, the debugging UI drives the
* evaluation, and we can only respond to events from the debug adapter.
*/
export class LocalQueryRun {
public constructor(
private readonly outputDir: QueryOutputDir,
private readonly localQueries: LocalQueries,
private readonly queryInfo: LocalQueryInfo,
private readonly dbItem: DatabaseItem,
public readonly logger: Logger, // Public so that other clients, like the debug adapter, know where to send log output
private readonly queryHistoryManager: QueryHistoryManager,
private readonly cliServer: CodeQLCliServer,
) {}
/**
* Updates the UI based on the results of the query evaluation. This creates the evaluator log
* summaries, updates the query history item for the evaluation with the results and evaluation
* time, and displays the results view.
*
* This function must be called when the evaluation completes, whether the evaluation was
* successful or not.
* */
public async complete(results: CoreQueryResults): Promise<void> {
const evalLogPaths = await this.summarizeEvalLog(
results.resultType,
this.outputDir,
this.logger,
);
if (evalLogPaths !== undefined) {
this.queryInfo.setEvaluatorLogPaths(evalLogPaths);
}
const queryWithResults = await this.getCompletedQueryInfo(results);
this.queryHistoryManager.completeQuery(this.queryInfo, queryWithResults);
await this.localQueries.showResultsForCompletedQuery(
this.queryInfo as CompletedLocalQueryInfo,
WebviewReveal.Forced,
);
// Note we must update the query history view after showing results as the
// display and sorting might depend on the number of results
await this.queryHistoryManager.refreshTreeView();
}
/**
* Updates the UI in the case where query evaluation throws an exception.
*/
public async fail(err: Error): Promise<void> {
err.message = `Error running query: ${err.message}`;
this.queryInfo.failureReason = err.message;
await this.queryHistoryManager.refreshTreeView();
}
/**
* Generate summaries of the structured evaluator log.
*/
private async summarizeEvalLog(
resultType: QueryResultType,
outputDir: QueryOutputDir,
logger: BaseLogger,
): Promise<EvaluatorLogPaths | undefined> {
const evalLogPaths = await generateEvalLogSummaries(
this.cliServer,
outputDir,
);
if (evalLogPaths !== undefined) {
if (evalLogPaths.endSummary !== undefined) {
void logEndSummary(evalLogPaths.endSummary, logger); // Logged asynchrnously
}
} else {
// Raw evaluator log was not found. Notify the user, unless we know why it wasn't found.
if (resultType === QueryResultType.SUCCESS) {
void showAndLogWarningMessage(
`Failed to write structured evaluator log to ${outputDir.evalLogPath}.`,
);
} else {
// Don't bother notifying the user if there's no log. For some errors, like compilation
// errors, we don't expect a log. For cancellations and OOM errors, whether or not we have
// a log depends on how far execution got before termination.
}
}
return evalLogPaths;
}
/**
* Gets a `QueryWithResults` containing information about the evaluation of the query and its
* result, in the form expected by the query history UI.
*/
private async getCompletedQueryInfo(
results: CoreQueryResults,
): Promise<QueryWithResults> {
// Read the query metadata if possible, to use in the UI.
const metadata = await tryGetQueryMetadata(
this.cliServer,
this.queryInfo.initialInfo.queryPath,
);
const query = new QueryEvaluationInfo(
this.outputDir.querySaveDir,
this.dbItem.databaseUri.fsPath,
await this.dbItem.hasMetadataFile(),
this.queryInfo.initialInfo.quickEvalPosition,
metadata,
);
if (results.resultType !== QueryResultType.SUCCESS) {
const message = results.message
? redactableError`Failed to run query: ${results.message}`
: redactableError`Failed to run query`;
void showAndLogExceptionWithTelemetry(message);
}
const message = formatResultMessage(results);
const successful = results.resultType === QueryResultType.SUCCESS;
return {
query,
result: {
evaluationTime: results.evaluationTime,
queryId: 0,
resultType: successful
? QueryResultType.SUCCESS
: QueryResultType.OTHER_ERROR,
runId: 0,
message,
},
message,
successful,
};
}
}

View File

@@ -16,6 +16,7 @@ import { ErrorLike } from "./errors";
import { DataFlowPaths } from "../variant-analysis/shared/data-flow-paths";
import { ExternalApiUsage } from "../data-extensions-editor/external-api-usage";
import { ModeledMethod } from "../data-extensions-editor/modeled-method";
import { DataExtensionEditorViewState } from "../data-extensions-editor/shared/view-state";
/**
* This module contains types and code that are shared between
@@ -481,10 +482,9 @@ export type ToDataFlowPathsMessage = SetDataFlowPathsMessage;
export type FromDataFlowPathsMessage = CommonFromViewMessages;
export interface SetDataExtensionEditorInitialDataMessage {
t: "setDataExtensionEditorInitialData";
extensionPackName: string;
modelFilename: string;
export interface SetExtensionPackStateMessage {
t: "setDataExtensionEditorViewState";
viewState: DataExtensionEditorViewState;
}
export interface SetExternalApiUsagesMessage {
@@ -536,7 +536,7 @@ export interface GenerateExternalApiMessage {
}
export type ToDataExtensionsEditorMessage =
| SetDataExtensionEditorInitialDataMessage
| SetExtensionPackStateMessage
| SetExternalApiUsagesMessage
| ShowProgressMessage
| AddModeledMethodsMessage;

View File

@@ -3,6 +3,12 @@ import {
RepositoryWithMetadata,
} from "../variant-analysis/shared/repository";
import { parseDate } from "./date";
import { assertNever } from "./helpers-pure";
export enum FilterKey {
All = "all",
WithResults = "withResults",
}
export enum SortKey {
Name = "name",
@@ -13,6 +19,7 @@ export enum SortKey {
export type RepositoriesFilterSortState = {
searchValue: string;
filterKey: FilterKey;
sortKey: SortKey;
};
@@ -22,20 +29,43 @@ export type RepositoriesFilterSortStateWithIds = RepositoriesFilterSortState & {
export const defaultFilterSortState: RepositoriesFilterSortState = {
searchValue: "",
filterKey: FilterKey.All,
sortKey: SortKey.Name,
};
export function matchesFilter(
repo: Pick<Repository, "fullName">,
item: FilterAndSortableResult,
filterSortState: RepositoriesFilterSortState | undefined,
): boolean {
if (!filterSortState) {
return true;
}
return repo.fullName
.toLowerCase()
.includes(filterSortState.searchValue.toLowerCase());
return (
matchesSearch(item.repository, filterSortState.searchValue) &&
matchesFilterKey(item.resultCount, filterSortState.filterKey)
);
}
function matchesSearch(
repository: SortableRepository,
searchValue: string,
): boolean {
return repository.fullName.toLowerCase().includes(searchValue.toLowerCase());
}
function matchesFilterKey(
resultCount: number | undefined,
filterKey: FilterKey,
): boolean {
switch (filterKey) {
case FilterKey.All:
return true;
case FilterKey.WithResults:
return resultCount !== undefined && resultCount > 0;
default:
assertNever(filterKey);
}
}
type SortableRepository = Pick<Repository, "fullName"> &
@@ -71,17 +101,22 @@ export function compareRepository(
};
}
type SortableResult = {
type FilterAndSortableResult = {
repository: SortableRepository;
resultCount?: number;
};
type FilterAndSortableResultWithIds = {
repository: SortableRepository & Pick<Repository, "id">;
resultCount?: number;
};
export function compareWithResults(
filterSortState: RepositoriesFilterSortState | undefined,
): (left: SortableResult, right: SortableResult) => number {
): (left: FilterAndSortableResult, right: FilterAndSortableResult) => number {
const fallbackSort = compareRepository(filterSortState);
return (left: SortableResult, right: SortableResult) => {
return (left: FilterAndSortableResult, right: FilterAndSortableResult) => {
// Highest to lowest
if (filterSortState?.sortKey === SortKey.ResultsCount) {
const resultCount = (right.resultCount ?? 0) - (left.resultCount ?? 0);
@@ -95,7 +130,7 @@ export function compareWithResults(
}
export function filterAndSortRepositoriesWithResultsByName<
T extends SortableResult,
T extends FilterAndSortableResult,
>(
repositories: T[] | undefined,
filterSortState: RepositoriesFilterSortState | undefined,
@@ -105,11 +140,13 @@ export function filterAndSortRepositoriesWithResultsByName<
}
return repositories
.filter((repo) => matchesFilter(repo.repository, filterSortState))
.filter((repo) => matchesFilter(repo, filterSortState))
.sort(compareWithResults(filterSortState));
}
export function filterAndSortRepositoriesWithResults<T extends SortableResult>(
export function filterAndSortRepositoriesWithResults<
T extends FilterAndSortableResultWithIds,
>(
repositories: T[] | undefined,
filterSortState: RepositoriesFilterSortStateWithIds | undefined,
): T[] | undefined {
@@ -117,6 +154,7 @@ export function filterAndSortRepositoriesWithResults<T extends SortableResult>(
return undefined;
}
// If repository IDs are given, then ignore the search value and filter key
if (
filterSortState?.repositoryIds &&
filterSortState.repositoryIds.length > 0

View File

@@ -1,6 +1,6 @@
import { Uri, window } from "vscode";
import { CodeQLCliServer } from "./cli";
import { QueryRunner } from "./queryRunner";
import { QueryRunner } from "./query-server";
import { basename, join } from "path";
import { getErrorMessage } from "./pure/helpers-pure";
import { redactableError } from "./pure/errors";

View File

@@ -49,7 +49,7 @@ import { EvalLogViewer } from "../eval-log-viewer";
import EvalLogTreeBuilder from "../eval-log-tree-builder";
import { EvalLogData, parseViewerData } from "../pure/log-summary-parser";
import { QueryWithResults } from "../run-queries-shared";
import { QueryRunner } from "../queryRunner";
import { QueryRunner } from "../query-server";
import { VariantAnalysisManager } from "../variant-analysis/variant-analysis-manager";
import { VariantAnalysisHistoryItem } from "./variant-analysis-history-item";
import { getTotalResultCount } from "../variant-analysis/shared/variant-analysis";
@@ -235,11 +235,8 @@ export class QueryHistoryManager extends DisposableObject {
"codeQLQueryHistory.sortByDate": this.handleSortByDate.bind(this),
"codeQLQueryHistory.sortByCount": this.handleSortByCount.bind(this),
"codeQLQueryHistory.openQueryTitleMenu": this.handleOpenQuery.bind(this),
"codeQLQueryHistory.openQueryContextMenu":
this.handleOpenQuery.bind(this),
"codeQLQueryHistory.removeHistoryItemTitleMenu":
this.handleRemoveHistoryItem.bind(this),
"codeQLQueryHistory.removeHistoryItemContextMenu":
this.handleRemoveHistoryItem.bind(this),
"codeQLQueryHistory.removeHistoryItemContextInline":

View File

@@ -21,7 +21,7 @@ import {
QueryEvaluationInfo,
QueryWithResults,
} from "./run-queries-shared";
import { formatLegacyMessage } from "./legacy-query-server/run-queries";
import { formatLegacyMessage } from "./query-server/legacy";
import { sarifParser } from "./sarif-parser";
/**

View File

@@ -0,0 +1,5 @@
export * from "./new-query-runner";
export * from "./query-runner";
export * from "./query-server-client";
export * from "./run-queries";
export * from "./server-process";

View File

@@ -0,0 +1,4 @@
export * from "./legacy-query-runner";
export * from "./query-server-client";
export * from "./run-queries";
export * from "./upgrades";

View File

@@ -1,16 +1,20 @@
import { CancellationToken } from "vscode";
import { CodeQLCliServer } from "../cli";
import { ProgressCallback } from "../progress";
import { Logger } from "../common";
import { DatabaseItem } from "../local-databases";
import { CodeQLCliServer } from "../../cli";
import { ProgressCallback } from "../../progress";
import { Logger } from "../../common";
import { DatabaseItem } from "../../local-databases";
import {
Dataset,
deregisterDatabases,
registerDatabases,
} from "../pure/legacy-messages";
import { CoreQueryResults, CoreQueryTarget, QueryRunner } from "../queryRunner";
import { QueryOutputDir } from "../run-queries-shared";
import { QueryServerClient } from "./queryserver-client";
} from "../../pure/legacy-messages";
import {
CoreQueryResults,
CoreQueryTarget,
QueryRunner,
} from "../query-runner";
import { QueryOutputDir } from "../../run-queries-shared";
import { QueryServerClient } from "./query-server-client";
import {
clearCacheInDatabase,
compileAndRunQueryAgainstDatabaseCore,

View File

@@ -1,21 +1,21 @@
import { ensureFile } from "fs-extra";
import { DisposableObject } from "../pure/disposable-object";
import { DisposableObject } from "../../pure/disposable-object";
import { CancellationToken } from "vscode";
import { createMessageConnection, RequestType } from "vscode-jsonrpc/node";
import * as cli from "../cli";
import { QueryServerConfig } from "../config";
import { Logger, ProgressReporter } from "../common";
import * as cli from "../../cli";
import { QueryServerConfig } from "../../config";
import { Logger, ProgressReporter } from "../../common";
import {
completeQuery,
EvaluationResult,
progress,
ProgressMessage,
WithProgressId,
} from "../pure/legacy-messages";
import { ProgressCallback, ProgressTask } from "../progress";
import { ServerProcess } from "../json-rpc-server";
import { App } from "../common/app";
} from "../../pure/legacy-messages";
import { ProgressCallback, ProgressTask } from "../../progress";
import { ServerProcess } from "../server-process";
import { App } from "../../common/app";
type WithProgressReporting = (
task: (

View File

@@ -3,29 +3,29 @@ import { basename } from "path";
import { CancellationToken, Uri } from "vscode";
import { LSPErrorCodes, ResponseError } from "vscode-languageclient";
import * as cli from "../cli";
import * as cli from "../../cli";
import {
DatabaseContentsWithDbScheme,
DatabaseItem,
DatabaseResolver,
} from "../local-databases";
} from "../../local-databases";
import {
showAndLogExceptionWithTelemetry,
showAndLogWarningMessage,
upgradesTmpDir,
} from "../helpers";
import { ProgressCallback } from "../progress";
import { QueryMetadata } from "../pure/interface-types";
import { extLogger, Logger } from "../common";
import * as messages from "../pure/legacy-messages";
import * as newMessages from "../pure/new-messages";
import * as qsClient from "./queryserver-client";
import { asError, getErrorMessage } from "../pure/helpers-pure";
} from "../../helpers";
import { ProgressCallback } from "../../progress";
import { QueryMetadata } from "../../pure/interface-types";
import { extLogger, Logger } from "../../common";
import * as messages from "../../pure/legacy-messages";
import * as newMessages from "../../pure/new-messages";
import * as qsClient from "./query-server-client";
import { asError, getErrorMessage } from "../../pure/helpers-pure";
import { compileDatabaseUpgradeSequence } from "./upgrades";
import { QueryEvaluationInfo, QueryOutputDir } from "../run-queries-shared";
import { redactableError } from "../pure/errors";
import { CoreQueryResults, CoreQueryTarget } from "../queryRunner";
import { Position } from "../pure/messages-shared";
import { QueryEvaluationInfo, QueryOutputDir } from "../../run-queries-shared";
import { redactableError } from "../../pure/errors";
import { CoreQueryResults, CoreQueryTarget } from "../query-runner";
import { Position } from "../../pure/messages-shared";
export async function compileQuery(
qs: qsClient.QueryServerClient,

View File

@@ -3,16 +3,16 @@ import {
getOnDiskWorkspaceFolders,
showAndLogExceptionWithTelemetry,
tmpDir,
} from "../helpers";
import { ProgressCallback, UserCancellationException } from "../progress";
import { extLogger } from "../common";
import * as messages from "../pure/legacy-messages";
import * as qsClient from "./queryserver-client";
} from "../../helpers";
import { ProgressCallback, UserCancellationException } from "../../progress";
import { extLogger } from "../../common";
import * as messages from "../../pure/legacy-messages";
import * as qsClient from "./query-server-client";
import * as tmp from "tmp-promise";
import { dirname } from "path";
import { DatabaseItem } from "../local-databases";
import { asError, getErrorMessage } from "../pure/helpers-pure";
import { redactableError } from "../pure/errors";
import { DatabaseItem } from "../../local-databases";
import { asError, getErrorMessage } from "../../pure/helpers-pure";
import { redactableError } from "../../pure/errors";
/**
* Maximum number of lines to include from database upgrade message,

View File

@@ -0,0 +1,165 @@
import { CancellationToken } from "vscode";
import { ProgressCallback, UserCancellationException } from "../progress";
import { DatabaseItem } from "../local-databases";
import {
clearCache,
ClearCacheParams,
clearPackCache,
deregisterDatabases,
registerDatabases,
upgradeDatabase,
} from "../pure/new-messages";
import { CoreQueryResults, CoreQueryTarget, QueryRunner } from "./query-runner";
import { QueryServerClient } from "./query-server-client";
import { compileAndRunQueryAgainstDatabaseCore } from "./run-queries";
import * as vscode from "vscode";
import { getOnDiskWorkspaceFolders } from "../helpers";
import { CodeQLCliServer } from "../cli";
import { Logger } from "../common";
import { QueryOutputDir } from "../run-queries-shared";
export class NewQueryRunner extends QueryRunner {
constructor(public readonly qs: QueryServerClient) {
super();
}
get cliServer(): CodeQLCliServer {
return this.qs.cliServer;
}
get customLogDirectory(): string | undefined {
return this.qs.config.customLogDirectory;
}
get logger(): Logger {
return this.qs.logger;
}
async restartQueryServer(
progress: ProgressCallback,
token: CancellationToken,
): Promise<void> {
await this.qs.restartQueryServer(progress, token);
}
onStart(
callBack: (
progress: ProgressCallback,
token: CancellationToken,
) => Promise<void>,
) {
this.qs.onDidStartQueryServer(callBack);
}
async clearCacheInDatabase(
dbItem: DatabaseItem,
progress: ProgressCallback,
token: CancellationToken,
): Promise<void> {
if (dbItem.contents === undefined) {
throw new Error("Can't clear the cache in an invalid database.");
}
const db = dbItem.databaseUri.fsPath;
const params: ClearCacheParams = {
dryRun: false,
db,
};
await this.qs.sendRequest(clearCache, params, token, progress);
}
public async compileAndRunQueryAgainstDatabaseCore(
dbPath: string,
query: CoreQueryTarget,
additionalPacks: string[],
extensionPacks: string[] | undefined,
generateEvalLog: boolean,
outputDir: QueryOutputDir,
progress: ProgressCallback,
token: CancellationToken,
templates: Record<string, string> | undefined,
logger: Logger,
): Promise<CoreQueryResults> {
return await compileAndRunQueryAgainstDatabaseCore(
this.qs,
dbPath,
query,
generateEvalLog,
additionalPacks,
extensionPacks,
outputDir,
progress,
token,
templates,
logger,
);
}
async deregisterDatabase(
progress: ProgressCallback,
token: CancellationToken,
dbItem: DatabaseItem,
): Promise<void> {
if (dbItem.contents) {
const databases: string[] = [dbItem.databaseUri.fsPath];
await this.qs.sendRequest(
deregisterDatabases,
{ databases },
token,
progress,
);
}
}
async registerDatabase(
progress: ProgressCallback,
token: CancellationToken,
dbItem: DatabaseItem,
): Promise<void> {
if (dbItem.contents) {
const databases: string[] = [dbItem.databaseUri.fsPath];
await this.qs.sendRequest(
registerDatabases,
{ databases },
token,
progress,
);
}
}
async clearPackCache(): Promise<void> {
await this.qs.sendRequest(clearPackCache, {});
}
async upgradeDatabaseExplicit(
dbItem: DatabaseItem,
progress: ProgressCallback,
token: CancellationToken,
): Promise<void> {
const yesItem = { title: "Yes", isCloseAffordance: false };
const noItem = { title: "No", isCloseAffordance: true };
const dialogOptions: vscode.MessageItem[] = [yesItem, noItem];
const message = `Should the database ${dbItem.databaseUri.fsPath} be destructively upgraded?\n\nThis should not be necessary to run queries
as we will non-destructively update it anyway.`;
const chosenItem = await vscode.window.showInformationMessage(
message,
{ modal: true },
...dialogOptions,
);
if (chosenItem !== yesItem) {
throw new UserCancellationException(
"User cancelled the database upgrade.",
);
}
await this.qs.sendRequest(
upgradeDatabase,
{
db: dbItem.databaseUri.fsPath,
additionalPacks: getOnDiskWorkspaceFolders(),
},
token,
progress,
);
}
}

View File

@@ -1,74 +1,72 @@
import { CancellationToken } from "vscode";
import { ProgressCallback, UserCancellationException } from "../progress";
import { DatabaseItem } from "../local-databases";
import {
clearCache,
ClearCacheParams,
clearPackCache,
deregisterDatabases,
registerDatabases,
upgradeDatabase,
} from "../pure/new-messages";
import { CoreQueryResults, CoreQueryTarget, QueryRunner } from "../queryRunner";
import { QueryServerClient } from "./queryserver-client";
import { compileAndRunQueryAgainstDatabaseCore } from "./run-queries";
import * as vscode from "vscode";
import { getOnDiskWorkspaceFolders } from "../helpers";
import { CodeQLCliServer } from "../cli";
import { Logger } from "../common";
import { ProgressCallback } from "../progress";
import { DatabaseItem } from "../local-databases";
import { QueryOutputDir } from "../run-queries-shared";
import { Position, QueryResultType } from "../pure/new-messages";
import { BaseLogger, Logger } from "../common";
import { basename, join } from "path";
import { nanoid } from "nanoid";
export class NewQueryRunner extends QueryRunner {
constructor(public readonly qs: QueryServerClient) {
super();
}
export interface CoreQueryTarget {
/** The full path to the query. */
queryPath: string;
/**
* Optional position of text to be used as QuickEval target. This need not be in the same file as
* `query`.
*/
quickEvalPosition?: Position;
}
get cliServer(): CodeQLCliServer {
return this.qs.cliServer;
}
export interface CoreQueryResults {
readonly resultType: QueryResultType;
readonly message: string | undefined;
readonly evaluationTime: number;
}
get customLogDirectory(): string | undefined {
return this.qs.config.customLogDirectory;
}
export interface CoreQueryRun {
readonly queryTarget: CoreQueryTarget;
readonly dbPath: string;
readonly id: string;
readonly outputDir: QueryOutputDir;
get logger(): Logger {
return this.qs.logger;
}
async restartQueryServer(
evaluate(
progress: ProgressCallback,
token: CancellationToken,
): Promise<void> {
await this.qs.restartQueryServer(progress, token);
}
logger: BaseLogger,
): Promise<CoreCompletedQuery>;
}
onStart(
callBack: (
/** Includes both the results of the query and the initial information from `CoreQueryRun`. */
export type CoreCompletedQuery = CoreQueryResults &
Omit<CoreQueryRun, "evaluate">;
export abstract class QueryRunner {
abstract restartQueryServer(
progress: ProgressCallback,
token: CancellationToken,
): Promise<void>;
abstract cliServer: CodeQLCliServer;
abstract customLogDirectory: string | undefined;
abstract logger: Logger;
abstract onStart(
arg0: (
progress: ProgressCallback,
token: CancellationToken,
) => Promise<void>,
) {
this.qs.onDidStartQueryServer(callBack);
}
async clearCacheInDatabase(
): void;
abstract clearCacheInDatabase(
dbItem: DatabaseItem,
progress: ProgressCallback,
token: CancellationToken,
): Promise<void> {
if (dbItem.contents === undefined) {
throw new Error("Can't clear the cache in an invalid database.");
}
): Promise<void>;
const db = dbItem.databaseUri.fsPath;
const params: ClearCacheParams = {
dryRun: false,
db,
};
await this.qs.sendRequest(clearCache, params, token, progress);
}
public async compileAndRunQueryAgainstDatabaseCore(
/**
* Overridden in subclasses to evaluate the query via the query server and return the results.
*/
public abstract compileAndRunQueryAgainstDatabaseCore(
dbPath: string,
query: CoreQueryTarget,
additionalPacks: string[],
@@ -78,88 +76,76 @@ export class NewQueryRunner extends QueryRunner {
progress: ProgressCallback,
token: CancellationToken,
templates: Record<string, string> | undefined,
logger: Logger,
): Promise<CoreQueryResults> {
return await compileAndRunQueryAgainstDatabaseCore(
this.qs,
logger: BaseLogger,
): Promise<CoreQueryResults>;
abstract deregisterDatabase(
progress: ProgressCallback,
token: CancellationToken,
dbItem: DatabaseItem,
): Promise<void>;
abstract registerDatabase(
progress: ProgressCallback,
token: CancellationToken,
dbItem: DatabaseItem,
): Promise<void>;
abstract upgradeDatabaseExplicit(
dbItem: DatabaseItem,
progress: ProgressCallback,
token: CancellationToken,
): Promise<void>;
abstract clearPackCache(): Promise<void>;
/**
* Create a `CoreQueryRun` object. This creates an object whose `evaluate()` function can be
* called to actually evaluate the query. The returned object also contains information about the
* query evaluation that is known even before evaluation starts, including the unique ID of the
* evaluation and the path to its output directory.
*/
public createQueryRun(
dbPath: string,
query: CoreQueryTarget,
generateEvalLog: boolean,
additionalPacks: string[],
extensionPacks: string[] | undefined,
queryStorageDir: string,
id = `${basename(query.queryPath)}-${nanoid()}`,
templates: Record<string, string> | undefined,
): CoreQueryRun {
const outputDir = new QueryOutputDir(join(queryStorageDir, id));
return {
queryTarget: query,
dbPath,
query,
generateEvalLog,
additionalPacks,
extensionPacks,
id,
outputDir,
progress,
token,
templates,
logger,
);
}
async deregisterDatabase(
progress: ProgressCallback,
token: CancellationToken,
dbItem: DatabaseItem,
): Promise<void> {
if (dbItem.contents) {
const databases: string[] = [dbItem.databaseUri.fsPath];
await this.qs.sendRequest(
deregisterDatabases,
{ databases },
token,
progress,
);
}
}
async registerDatabase(
progress: ProgressCallback,
token: CancellationToken,
dbItem: DatabaseItem,
): Promise<void> {
if (dbItem.contents) {
const databases: string[] = [dbItem.databaseUri.fsPath];
await this.qs.sendRequest(
registerDatabases,
{ databases },
token,
progress,
);
}
}
async clearPackCache(): Promise<void> {
await this.qs.sendRequest(clearPackCache, {});
}
async upgradeDatabaseExplicit(
dbItem: DatabaseItem,
progress: ProgressCallback,
token: CancellationToken,
): Promise<void> {
const yesItem = { title: "Yes", isCloseAffordance: false };
const noItem = { title: "No", isCloseAffordance: true };
const dialogOptions: vscode.MessageItem[] = [yesItem, noItem];
const message = `Should the database ${dbItem.databaseUri.fsPath} be destructively upgraded?\n\nThis should not be necessary to run queries
as we will non-destructively update it anyway.`;
const chosenItem = await vscode.window.showInformationMessage(
message,
{ modal: true },
...dialogOptions,
);
if (chosenItem !== yesItem) {
throw new UserCancellationException(
"User cancelled the database upgrade.",
);
}
await this.qs.sendRequest(
upgradeDatabase,
{
db: dbItem.databaseUri.fsPath,
additionalPacks: getOnDiskWorkspaceFolders(),
evaluate: async (
progress: ProgressCallback,
token: CancellationToken,
logger: BaseLogger,
): Promise<CoreCompletedQuery> => {
return {
id,
outputDir,
dbPath,
queryTarget: query,
...(await this.compileAndRunQueryAgainstDatabaseCore(
dbPath,
query,
additionalPacks,
extensionPacks,
generateEvalLog,
outputDir,
progress,
token,
templates,
logger,
)),
};
},
token,
progress,
);
};
}
}

View File

@@ -12,7 +12,7 @@ import {
WithProgressId,
} from "../pure/new-messages";
import { ProgressCallback, ProgressTask } from "../progress";
import { ServerProcess } from "../json-rpc-server";
import { ServerProcess } from "./server-process";
import { App } from "../common/app";
type ServerOpts = {

View File

@@ -2,8 +2,8 @@ import { CancellationToken } from "vscode";
import { ProgressCallback } from "../progress";
import * as messages from "../pure/new-messages";
import { QueryOutputDir } from "../run-queries-shared";
import * as qsClient from "./queryserver-client";
import { CoreQueryResults, CoreQueryTarget } from "../queryRunner";
import * as qsClient from "./query-server-client";
import { CoreQueryResults, CoreQueryTarget } from "./query-runner";
import { Logger } from "../common";
/**

View File

@@ -1,4 +1,4 @@
import { Logger } from "./common";
import { Logger } from "../common";
import * as cp from "child_process";
import { Disposable } from "vscode";
import { MessageConnection } from "vscode-jsonrpc";

View File

@@ -1,151 +0,0 @@
import { CancellationToken } from "vscode";
import { CodeQLCliServer } from "./cli";
import { ProgressCallback } from "./progress";
import { DatabaseItem } from "./local-databases";
import { QueryOutputDir } from "./run-queries-shared";
import { Position, QueryResultType } from "./pure/new-messages";
import { BaseLogger, Logger } from "./common";
import { basename, join } from "path";
import { nanoid } from "nanoid";
export interface CoreQueryTarget {
/** The full path to the query. */
queryPath: string;
/**
* Optional position of text to be used as QuickEval target. This need not be in the same file as
* `query`.
*/
quickEvalPosition?: Position;
}
export interface CoreQueryResults {
readonly resultType: QueryResultType;
readonly message: string | undefined;
readonly evaluationTime: number;
}
export interface CoreQueryRun {
readonly queryTarget: CoreQueryTarget;
readonly dbPath: string;
readonly id: string;
readonly outputDir: QueryOutputDir;
evaluate(
progress: ProgressCallback,
token: CancellationToken,
logger: BaseLogger,
): Promise<CoreCompletedQuery>;
}
/** Includes both the results of the query and the initial information from `CoreQueryRun`. */
export type CoreCompletedQuery = CoreQueryResults &
Omit<CoreQueryRun, "evaluate">;
export abstract class QueryRunner {
abstract restartQueryServer(
progress: ProgressCallback,
token: CancellationToken,
): Promise<void>;
abstract cliServer: CodeQLCliServer;
abstract customLogDirectory: string | undefined;
abstract logger: Logger;
abstract onStart(
arg0: (
progress: ProgressCallback,
token: CancellationToken,
) => Promise<void>,
): void;
abstract clearCacheInDatabase(
dbItem: DatabaseItem,
progress: ProgressCallback,
token: CancellationToken,
): Promise<void>;
/**
* Overridden in subclasses to evaluate the query via the query server and return the results.
*/
public abstract compileAndRunQueryAgainstDatabaseCore(
dbPath: string,
query: CoreQueryTarget,
additionalPacks: string[],
extensionPacks: string[] | undefined,
generateEvalLog: boolean,
outputDir: QueryOutputDir,
progress: ProgressCallback,
token: CancellationToken,
templates: Record<string, string> | undefined,
logger: BaseLogger,
): Promise<CoreQueryResults>;
abstract deregisterDatabase(
progress: ProgressCallback,
token: CancellationToken,
dbItem: DatabaseItem,
): Promise<void>;
abstract registerDatabase(
progress: ProgressCallback,
token: CancellationToken,
dbItem: DatabaseItem,
): Promise<void>;
abstract upgradeDatabaseExplicit(
dbItem: DatabaseItem,
progress: ProgressCallback,
token: CancellationToken,
): Promise<void>;
abstract clearPackCache(): Promise<void>;
/**
* Create a `CoreQueryRun` object. This creates an object whose `evaluate()` function can be
* called to actually evaluate the query. The returned object also contains information about the
* query evaluation that is known even before evaluation starts, including the unique ID of the
* evaluation and the path to its output directory.
*/
public createQueryRun(
dbPath: string,
query: CoreQueryTarget,
generateEvalLog: boolean,
additionalPacks: string[],
extensionPacks: string[] | undefined,
queryStorageDir: string,
id = `${basename(query.queryPath)}-${nanoid()}`,
templates: Record<string, string> | undefined,
): CoreQueryRun {
const outputDir = new QueryOutputDir(join(queryStorageDir, id));
return {
queryTarget: query,
dbPath,
id,
outputDir,
evaluate: async (
progress: ProgressCallback,
token: CancellationToken,
logger: BaseLogger,
): Promise<CoreCompletedQuery> => {
return {
id,
outputDir,
dbPath,
queryTarget: query,
...(await this.compileAndRunQueryAgainstDatabaseCore(
dbPath,
query,
additionalPacks,
extensionPacks,
generateEvalLog,
outputDir,
progress,
token,
templates,
logger,
)),
};
},
};
}
}

View File

@@ -14,7 +14,12 @@ import { QlPackGenerator } from "./qlpack-generator";
import { DatabaseItem, DatabaseManager } from "./local-databases";
import { ProgressCallback, UserCancellationException } from "./progress";
import { askForGitHubRepo, downloadGitHubDatabase } from "./databaseFetcher";
import { existsSync } from "fs";
import {
getSkeletonWizardFolder,
isCodespacesTemplate,
setSkeletonWizardFolder,
} from "./config";
import { existsSync } from "fs-extra";
type QueryLanguagesToDatabaseMap = Record<string, string>;
@@ -55,7 +60,7 @@ export class SkeletonQueryWizard {
return;
}
this.qlPackStoragePath = getFirstWorkspaceFolder();
this.qlPackStoragePath = await this.determineStoragePath();
const skeletonPackAlreadyExists =
existsSync(join(this.qlPackStoragePath, this.folderName)) ||
@@ -97,6 +102,38 @@ export class SkeletonQueryWizard {
});
}
public async determineStoragePath() {
const firstStorageFolder = getFirstWorkspaceFolder();
if (isCodespacesTemplate()) {
return firstStorageFolder;
}
let storageFolder = getSkeletonWizardFolder();
if (storageFolder === undefined || !existsSync(storageFolder)) {
storageFolder = await Window.showInputBox({
title:
"Please choose a folder in which to create your new query pack. You can change this in the extension settings.",
value: firstStorageFolder,
ignoreFocusOut: true,
});
}
if (storageFolder === undefined) {
throw new UserCancellationException("No storage folder entered.");
}
if (!existsSync(storageFolder)) {
throw new UserCancellationException(
"Invalid folder. Must be a folder that already exists.",
);
}
await setSkeletonWizardFolder(storageFolder);
return storageFolder;
}
private async chooseLanguage() {
this.progress({
message: "Choose language",
@@ -216,11 +253,19 @@ export class SkeletonQueryWizard {
}
private async selectOrDownloadDatabase() {
if (this.language === undefined) {
throw new Error("Language is undefined");
}
if (this.qlPackStoragePath === undefined) {
throw new Error("QL Pack storage path is undefined");
}
const existingDatabaseItem = await this.findExistingDatabaseItem();
const existingDatabaseItem =
await SkeletonQueryWizard.findExistingDatabaseItem(
this.language,
this.databaseManager.databaseItems,
);
if (existingDatabaseItem) {
// select the found database
@@ -231,59 +276,68 @@ export class SkeletonQueryWizard {
}
}
public async findDatabaseItemByNwo(
public static async findDatabaseItemByNwo(
language: string,
databaseNwo: string,
databaseItems: readonly DatabaseItem[],
): Promise<DatabaseItem | undefined> {
const dbItems = databaseItems || [];
const dbs = dbItems.filter(
(db) =>
db.language === language &&
db.name === databaseNwo &&
db.error === undefined,
const dbs = databaseItems.filter(
(db) => db.language === language && db.name === databaseNwo,
);
if (dbs.length === 0) {
return undefined;
}
return dbs[0];
return dbs.pop();
}
public async findDatabaseItemByLanguage(
public static async findDatabaseItemByLanguage(
language: string,
databaseItems: readonly DatabaseItem[],
): Promise<DatabaseItem | undefined> {
const dbItems = databaseItems || [];
const dbs = dbItems.filter(
(db) => db.language === language && db.error === undefined,
);
if (dbs.length === 0) {
return undefined;
}
return dbs[0];
const dbs = databaseItems.filter((db) => db.language === language);
return dbs.pop();
}
private async findExistingDatabaseItem() {
if (this.language === undefined) {
throw new Error("Language is undefined");
}
public static async findExistingDatabaseItem(
language: string,
databaseItems: readonly DatabaseItem[],
): Promise<DatabaseItem | undefined> {
const defaultDatabaseNwo = QUERY_LANGUAGE_TO_DATABASE_REPO[language];
const defaultDatabaseNwo = QUERY_LANGUAGE_TO_DATABASE_REPO[this.language];
const dbItems = await SkeletonQueryWizard.sortDatabaseItemsByDateAdded(
databaseItems,
);
const defaultDatabaseItem = await this.findDatabaseItemByNwo(
this.language,
const defaultDatabaseItem = await SkeletonQueryWizard.findDatabaseItemByNwo(
language,
defaultDatabaseNwo,
this.databaseManager.databaseItems,
dbItems,
);
if (defaultDatabaseItem !== undefined) {
return defaultDatabaseItem;
}
return await this.findDatabaseItemByLanguage(
this.language,
this.databaseManager.databaseItems,
return await SkeletonQueryWizard.findDatabaseItemByLanguage(
language,
dbItems,
);
}
public static async sortDatabaseItemsByDateAdded(
databaseItems: readonly DatabaseItem[],
) {
const validDbItems = databaseItems.filter((db) => db.error === undefined);
return validDbItems.sort((a, b) => {
if (a.dateAdded === undefined) {
return -1;
}
if (b.dateAdded === undefined) {
return 1;
}
return a.dateAdded - b.dateAdded;
});
}
}

View File

@@ -15,9 +15,22 @@ const Template: ComponentStory<typeof DataExtensionsEditorComponent> = (
export const DataExtensionsEditor = Template.bind({});
DataExtensionsEditor.args = {
initialExtensionPackName: "codeql/sql2o-models",
initialModelFilename:
"/home/user/vscode-codeql-starter/codeql-custom-queries-java/sql2o/models/sql2o.yml",
initialViewState: {
extensionPackModelFile: {
extensionPack: {
path: "/home/user/vscode-codeql-starter/codeql-custom-queries-java/sql2o",
yamlPath:
"/home/user/vscode-codeql-starter/codeql-custom-queries-java/sql2o/codeql-pack.yml",
name: "codeql/sql2o-models",
version: "0.0.0",
extensionTargets: {},
dataExtensions: [],
},
filename:
"/home/user/vscode-codeql-starter/codeql-custom-queries-java/sql2o/models/sql2o.yml",
},
modelFileExists: true,
},
initialExternalApiUsages: [
{
signature: "org.sql2o.Connection#createQuery(String)",

View File

@@ -0,0 +1,25 @@
import * as React from "react";
import { useState } from "react";
import { ComponentMeta } from "@storybook/react";
import { RepositoriesFilter as RepositoriesFilterComponent } from "../../view/variant-analysis/RepositoriesFilter";
import { FilterKey } from "../../pure/variant-analysis-filter-sort";
export default {
title: "Variant Analysis/Repositories Filter",
component: RepositoriesFilterComponent,
argTypes: {
value: {
control: {
disable: true,
},
},
},
} as ComponentMeta<typeof RepositoriesFilterComponent>;
export const RepositoriesFilter = () => {
const [value, setValue] = useState(FilterKey.All);
return <RepositoriesFilterComponent value={value} onChange={setValue} />;
};

View File

@@ -20,6 +20,7 @@ import { calculateModeledPercentage } from "./modeled";
import { LinkIconButton } from "../variant-analysis/LinkIconButton";
import { basename } from "../common/path";
import { ViewTitle } from "../common";
import { DataExtensionEditorViewState } from "../../data-extensions-editor/shared/view-state";
const DataExtensionsEditorContainer = styled.div`
margin-top: 1rem;
@@ -31,6 +32,12 @@ const DetailsContainer = styled.div`
align-items: center;
`;
const NonExistingModelFileContainer = styled.div`
display: flex;
gap: 0.2em;
align-items: center;
`;
const EditorContainer = styled.div`
margin-top: 1rem;
`;
@@ -47,24 +54,19 @@ const ProgressBar = styled.div<ProgressBarProps>`
`;
type Props = {
initialExtensionPackName?: string;
initialModelFilename?: string;
initialViewState?: DataExtensionEditorViewState;
initialExternalApiUsages?: ExternalApiUsage[];
initialModeledMethods?: Record<string, ModeledMethod>;
};
export function DataExtensionsEditor({
initialExtensionPackName,
initialModelFilename,
initialViewState,
initialExternalApiUsages = [],
initialModeledMethods = {},
}: Props): JSX.Element {
const [extensionPackName, setExtensionPackName] = useState<
string | undefined
>(initialExtensionPackName);
const [modelFilename, setModelFilename] = useState<string | undefined>(
initialModelFilename,
);
const [viewState, setViewState] = useState<
DataExtensionEditorViewState | undefined
>(initialViewState);
const [externalApiUsages, setExternalApiUsages] = useState<
ExternalApiUsage[]
@@ -83,9 +85,8 @@ export function DataExtensionsEditor({
if (evt.origin === window.origin) {
const msg: ToDataExtensionsEditorMessage = evt.data;
switch (msg.t) {
case "setDataExtensionEditorInitialData":
setExtensionPackName(msg.extensionPackName);
setModelFilename(msg.modelFilename);
case "setDataExtensionEditorViewState":
setViewState(msg.viewState);
break;
case "setExternalApiUsages":
setExternalApiUsages(msg.externalApiUsages);
@@ -181,17 +182,27 @@ export function DataExtensionsEditor({
<>
<ViewTitle>Data extensions editor</ViewTitle>
<DetailsContainer>
{extensionPackName && (
<LinkIconButton onClick={onOpenExtensionPackClick}>
<span slot="start" className="codicon codicon-package"></span>
{extensionPackName}
</LinkIconButton>
)}
{modelFilename && (
<LinkIconButton onClick={onOpenModelFileClick}>
<span slot="start" className="codicon codicon-file-code"></span>
{basename(modelFilename)}
</LinkIconButton>
{viewState?.extensionPackModelFile && (
<>
<LinkIconButton onClick={onOpenExtensionPackClick}>
<span slot="start" className="codicon codicon-package"></span>
{viewState.extensionPackModelFile.extensionPack.name}
</LinkIconButton>
{viewState.modelFileExists ? (
<LinkIconButton onClick={onOpenModelFileClick}>
<span
slot="start"
className="codicon codicon-file-code"
></span>
{basename(viewState.extensionPackModelFile.filename)}
</LinkIconButton>
) : (
<NonExistingModelFileContainer>
<span className="codicon codicon-file-code"></span>
{basename(viewState.extensionPackModelFile.filename)}
</NonExistingModelFileContainer>
)}
</>
)}
<div>{modeledPercentage.toFixed(2)}% modeled</div>
<div>{unModeledPercentage.toFixed(2)}% unmodeled</div>

View File

@@ -0,0 +1,36 @@
import * as React from "react";
import { useCallback } from "react";
import styled from "styled-components";
import { VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react";
import { Codicon } from "../common";
import { FilterKey } from "../../pure/variant-analysis-filter-sort";
const Dropdown = styled(VSCodeDropdown)`
width: 100%;
`;
type Props = {
value: FilterKey;
onChange: (value: FilterKey) => void;
className?: string;
};
export const RepositoriesFilter = ({ value, onChange, className }: Props) => {
const handleInput = useCallback(
(e: InputEvent) => {
const target = e.target as HTMLSelectElement;
onChange(target.value as FilterKey);
},
[onChange],
);
return (
<Dropdown value={value} onInput={handleInput} className={className}>
<Codicon name="list-filter" label="Filter..." slot="indicator" />
<VSCodeOption value={FilterKey.All}>All</VSCodeOption>
<VSCodeOption value={FilterKey.WithResults}>With results</VSCodeOption>
</Dropdown>
);
};

View File

@@ -2,11 +2,13 @@ import * as React from "react";
import { Dispatch, SetStateAction, useCallback } from "react";
import styled from "styled-components";
import {
FilterKey,
RepositoriesFilterSortState,
SortKey,
} from "../../pure/variant-analysis-filter-sort";
import { RepositoriesSearch } from "./RepositoriesSearch";
import { RepositoriesSort } from "./RepositoriesSort";
import { RepositoriesFilter } from "./RepositoriesFilter";
type Props = {
value: RepositoriesFilterSortState;
@@ -25,6 +27,10 @@ const RepositoriesSearchColumn = styled(RepositoriesSearch)`
flex: 3;
`;
const RepositoriesFilterColumn = styled(RepositoriesFilter)`
flex: 1;
`;
const RepositoriesSortColumn = styled(RepositoriesSort)`
flex: 1;
`;
@@ -40,6 +46,16 @@ export const RepositoriesSearchSortRow = ({ value, onChange }: Props) => {
[onChange],
);
const handleFilterKeyChange = useCallback(
(filterKey: FilterKey) => {
onChange((oldValue) => ({
...oldValue,
filterKey,
}));
},
[onChange],
);
const handleSortKeyChange = useCallback(
(sortKey: SortKey) => {
onChange((oldValue) => ({
@@ -56,6 +72,10 @@ export const RepositoriesSearchSortRow = ({ value, onChange }: Props) => {
value={value.searchValue}
onChange={handleSearchValueChange}
/>
<RepositoriesFilterColumn
value={value.filterKey}
onChange={handleFilterKeyChange}
/>
<RepositoriesSortColumn
value={value.sortKey}
onChange={handleSortKeyChange}

View File

@@ -56,8 +56,8 @@ export const VariantAnalysisSkippedRepositoriesTab = ({
}: VariantAnalysisSkippedRepositoriesTabProps) => {
const repositories = useMemo(() => {
return skippedRepositoryGroup.repositories
?.filter((repo) => {
return matchesFilter(repo, filterSortState);
?.filter((repository) => {
return matchesFilter({ repository }, filterSortState);
})
?.sort(compareRepository(filterSortState));
}, [filterSortState, skippedRepositoryGroup.repositories]);

View File

@@ -4,6 +4,7 @@ import {
defaultFilterSortState,
filterAndSortRepositoriesWithResults,
filterAndSortRepositoriesWithResultsByName,
FilterKey,
matchesFilter,
SortKey,
} from "../../src/pure/variant-analysis-filter-sort";
@@ -13,32 +14,93 @@ describe(matchesFilter.name, () => {
fullName: "github/codeql",
};
const testCases = [
{ searchValue: "", matches: true },
{ searchValue: "github/codeql", matches: true },
{ searchValue: "github", matches: true },
{ searchValue: "git", matches: true },
{ searchValue: "codeql", matches: true },
{ searchValue: "code", matches: true },
{ searchValue: "ql", matches: true },
{ searchValue: "/", matches: true },
{ searchValue: "gothub/codeql", matches: false },
{ searchValue: "hello", matches: false },
{ searchValue: "cod*ql", matches: false },
{ searchValue: "cod?ql", matches: false },
];
describe("searchValue", () => {
const testCases = [
{ searchValue: "", matches: true },
{ searchValue: "github/codeql", matches: true },
{ searchValue: "github", matches: true },
{ searchValue: "git", matches: true },
{ searchValue: "codeql", matches: true },
{ searchValue: "code", matches: true },
{ searchValue: "ql", matches: true },
{ searchValue: "/", matches: true },
{ searchValue: "gothub/codeql", matches: false },
{ searchValue: "hello", matches: false },
{ searchValue: "cod*ql", matches: false },
{ searchValue: "cod?ql", matches: false },
];
test.each(testCases)(
"returns $matches if searching for $searchValue",
({ searchValue, matches }) => {
test.each(testCases)(
"returns $matches if searching for $searchValue",
({ searchValue, matches }) => {
expect(
matchesFilter(
{ repository },
{
...defaultFilterSortState,
searchValue,
},
),
).toBe(matches);
},
);
});
describe("filterKey", () => {
it("returns true if filterKey is all and resultCount is positive", () => {
expect(
matchesFilter(repository, {
...defaultFilterSortState,
searchValue,
}),
).toBe(matches);
},
);
matchesFilter(
{ repository, resultCount: 1 },
{ ...defaultFilterSortState, filterKey: FilterKey.All },
),
).toBe(true);
});
it("returns true if filterKey is all and resultCount is zero", () => {
expect(
matchesFilter(
{ repository, resultCount: 0 },
{ ...defaultFilterSortState, filterKey: FilterKey.All },
),
).toBe(true);
});
it("returns true if filterKey is all and resultCount is undefined", () => {
expect(
matchesFilter(
{ repository },
{ ...defaultFilterSortState, filterKey: FilterKey.All },
),
).toBe(true);
});
it("returns true if filterKey is withResults and resultCount is positive", () => {
expect(
matchesFilter(
{ repository, resultCount: 1 },
{ ...defaultFilterSortState, filterKey: FilterKey.WithResults },
),
).toBe(true);
});
it("returns false if filterKey is withResults and resultCount is zero", () => {
expect(
matchesFilter(
{ repository, resultCount: 0 },
{ ...defaultFilterSortState, filterKey: FilterKey.WithResults },
),
).toBe(false);
});
it("returns false if filterKey is withResults and resultCount is undefined", () => {
expect(
matchesFilter(
{ repository },
{ ...defaultFilterSortState, filterKey: FilterKey.WithResults },
),
).toBe(false);
});
});
});
describe(compareRepository.name, () => {
@@ -349,7 +411,7 @@ describe(filterAndSortRepositoriesWithResultsByName.name, () => {
},
];
describe("when sort key is given without filter", () => {
describe("when sort key is given without search or filter", () => {
it("returns the correct results", () => {
expect(
filterAndSortRepositoriesWithResultsByName(repositories, {
@@ -365,7 +427,7 @@ describe(filterAndSortRepositoriesWithResultsByName.name, () => {
});
});
describe("when sort key and search filter are given", () => {
describe("when sort key and search are given without filter", () => {
it("returns the correct results", () => {
expect(
filterAndSortRepositoriesWithResultsByName(repositories, {
@@ -376,6 +438,30 @@ describe(filterAndSortRepositoriesWithResultsByName.name, () => {
).toEqual([repositories[2], repositories[0]]);
});
});
describe("when sort key and filter withResults are given without search", () => {
it("returns the correct results", () => {
expect(
filterAndSortRepositoriesWithResultsByName(repositories, {
...defaultFilterSortState,
sortKey: SortKey.ResultsCount,
filterKey: FilterKey.WithResults,
}),
).toEqual([repositories[3], repositories[2], repositories[0]]);
});
});
describe("when sort key, search, and filter withResults are given", () => {
it("returns the correct results", () => {
expect(
filterAndSortRepositoriesWithResultsByName(repositories, {
sortKey: SortKey.ResultsCount,
filterKey: FilterKey.WithResults,
searchValue: "r",
}),
).toEqual([repositories[3]]);
});
});
});
describe(filterAndSortRepositoriesWithResults.name, () => {
@@ -410,7 +496,7 @@ describe(filterAndSortRepositoriesWithResults.name, () => {
},
];
describe("when sort key is given without filter", () => {
describe("when sort key is given", () => {
it("returns the correct results", () => {
expect(
filterAndSortRepositoriesWithResults(repositories, {
@@ -426,7 +512,7 @@ describe(filterAndSortRepositoriesWithResults.name, () => {
});
});
describe("when sort key and search filter are given", () => {
describe("when sort key and search are given", () => {
it("returns the correct results", () => {
expect(
filterAndSortRepositoriesWithResults(repositories, {
@@ -438,12 +524,49 @@ describe(filterAndSortRepositoriesWithResults.name, () => {
});
});
describe("when sort key, search filter, and repository ids are given", () => {
describe("when sort key and filter withResults are given", () => {
it("returns the correct results", () => {
expect(
filterAndSortRepositoriesWithResults(repositories, {
...defaultFilterSortState,
sortKey: SortKey.ResultsCount,
filterKey: FilterKey.WithResults,
}),
).toEqual([repositories[3], repositories[2], repositories[0]]);
});
});
describe("when sort key and filter withResults are given", () => {
it("returns the correct results", () => {
expect(
filterAndSortRepositoriesWithResults(repositories, {
...defaultFilterSortState,
sortKey: SortKey.ResultsCount,
filterKey: FilterKey.WithResults,
}),
).toEqual([repositories[3], repositories[2], repositories[0]]);
});
});
describe("when sort key, search, and filter withResults are given", () => {
it("returns the correct results", () => {
expect(
filterAndSortRepositoriesWithResults(repositories, {
...defaultFilterSortState,
sortKey: SortKey.ResultsCount,
filterKey: FilterKey.WithResults,
searchValue: "r",
}),
).toEqual([repositories[3]]);
});
});
describe("when sort key, search, filter withResults, and repository ids are given", () => {
it("returns the correct results", () => {
expect(
filterAndSortRepositoriesWithResults(repositories, {
sortKey: SortKey.ResultsCount,
filterKey: FilterKey.WithResults,
searchValue: "la",
repositoryIds: [
repositories[1].repository.id,

View File

@@ -10,7 +10,7 @@ import {
import * as CodeQLProtocol from "../../../../src/debugger/debug-protocol";
import { DisposableObject } from "../../../../src/pure/disposable-object";
import { QueryResultType } from "../../../../src/pure/legacy-messages";
import { CoreCompletedQuery } from "../../../../src/queryRunner";
import { CoreCompletedQuery } from "../../../../src/query-server/query-runner";
import { QueryOutputDir } from "../../../../src/run-queries-shared";
import {
QLDebugArgs,

View File

@@ -4,11 +4,11 @@ import { dirSync } from "tmp";
import { pathToFileURL } from "url";
import { CancellationTokenSource } from "vscode-jsonrpc";
import * as messages from "../../../src/pure/legacy-messages";
import * as qsClient from "../../../src/legacy-query-server/queryserver-client";
import * as qsClient from "../../../src/query-server/legacy/query-server-client";
import * as cli from "../../../src/cli";
import { CellValue } from "../../../src/pure/bqrs-cli-types";
import { describeWithCodeQL } from "../cli";
import { QueryServerClient } from "../../../src/legacy-query-server/queryserver-client";
import { QueryServerClient } from "../../../src/query-server/legacy/query-server-client";
import { extLogger, ProgressReporter } from "../../../src/common";
import { createMockApp } from "../../__mocks__/appMock";
import { getActivatedExtension } from "../global.helper";

View File

@@ -2,11 +2,11 @@ import { join, basename } from "path";
import { dirSync } from "tmp";
import { CancellationTokenSource } from "vscode-jsonrpc";
import * as messages from "../../../src/pure/new-messages";
import * as qsClient from "../../../src/query-server/queryserver-client";
import * as qsClient from "../../../src/query-server/query-server-client";
import * as cli from "../../../src/cli";
import { CellValue } from "../../../src/pure/bqrs-cli-types";
import { describeWithCodeQL } from "../cli";
import { QueryServerClient } from "../../../src/query-server/queryserver-client";
import { QueryServerClient } from "../../../src/query-server/query-server-client";
import { extLogger, ProgressReporter } from "../../../src/common";
import { QueryResultType } from "../../../src/pure/new-messages";
import { ensureTestDatabase, getActivatedExtension } from "../global.helper";

View File

@@ -17,7 +17,10 @@ import {
} from "../global.helper";
import { CliVersionConstraint, CodeQLCliServer } from "../../../src/cli";
import { describeWithCodeQL } from "../cli";
import { CoreCompletedQuery, QueryRunner } from "../../../src/queryRunner";
import {
CoreCompletedQuery,
QueryRunner,
} from "../../../src/query-server/query-runner";
import { SELECT_QUERY_NAME } from "../../../src/contextual/locationFinder";
import { LocalQueries } from "../../../src/local-queries";
import { QueryResultType } from "../../../src/pure/new-messages";

View File

@@ -21,6 +21,7 @@ import {
import * as databaseFetcher from "../../../src/databaseFetcher";
import { createMockDB } from "../../factories/databases/databases";
import { asError } from "../../../src/pure/helpers-pure";
import { Setting } from "../../../src/config";
describe("SkeletonQueryWizard", () => {
let mockCli: CodeQLCliServer;
@@ -29,6 +30,7 @@ describe("SkeletonQueryWizard", () => {
let dir: tmp.DirResult;
let storagePath: string;
let quickPickSpy: jest.SpiedFunction<typeof window.showQuickPick>;
let showInputBoxSpy: jest.SpiedFunction<typeof window.showInputBox>;
let generateSpy: jest.SpiedFunction<
typeof QlPackGenerator.prototype.generate
>;
@@ -93,6 +95,9 @@ describe("SkeletonQueryWizard", () => {
quickPickSpy = jest
.spyOn(window, "showQuickPick")
.mockResolvedValueOnce(mockedQuickPickItem(chosenLanguage));
showInputBoxSpy = jest
.spyOn(window, "showInputBox")
.mockResolvedValue(storagePath);
generateSpy = jest
.spyOn(QlPackGenerator.prototype, "generate")
.mockResolvedValue(undefined);
@@ -315,7 +320,7 @@ describe("SkeletonQueryWizard", () => {
jest.spyOn(mockDbItem, "name", "get").mockReturnValue("mock-name");
const databaseItem = await wizard.findDatabaseItemByNwo(
const databaseItem = await SkeletonQueryWizard.findDatabaseItemByNwo(
mockDbItem.language,
mockDbItem.name,
[mockDbItem, mockDbItem2],
@@ -325,37 +330,6 @@ describe("SkeletonQueryWizard", () => {
JSON.stringify(mockDbItem),
);
});
it("should ignore databases with errors", async () => {
const mockDbItem = createMockDB(dir, {
language: "ruby",
dateAdded: 123,
} as FullDatabaseOptions);
const mockDbItem2 = createMockDB(dir, {
language: "javascript",
} as FullDatabaseOptions);
const mockDbItem3 = createMockDB(dir, {
language: "ruby",
dateAdded: 345,
} as FullDatabaseOptions);
jest.spyOn(mockDbItem, "name", "get").mockReturnValue("mock-name");
jest.spyOn(mockDbItem3, "name", "get").mockReturnValue(mockDbItem.name);
jest
.spyOn(mockDbItem, "error", "get")
.mockReturnValue(asError("database go boom!"));
const databaseItem = await wizard.findDatabaseItemByNwo(
mockDbItem.language,
mockDbItem.name,
[mockDbItem, mockDbItem2, mockDbItem3],
);
expect(JSON.stringify(databaseItem)).toEqual(
JSON.stringify(mockDbItem3),
);
});
});
describe("when the item doesn't exist", () => {
@@ -363,7 +337,7 @@ describe("SkeletonQueryWizard", () => {
const mockDbItem = createMockDB(dir);
const mockDbItem2 = createMockDB(dir);
const databaseItem = await wizard.findDatabaseItemByNwo(
const databaseItem = await SkeletonQueryWizard.findDatabaseItemByNwo(
"ruby",
"mock-nwo",
[mockDbItem, mockDbItem2],
@@ -384,39 +358,14 @@ describe("SkeletonQueryWizard", () => {
language: "javascript",
} as FullDatabaseOptions);
const databaseItem = await wizard.findDatabaseItemByLanguage("ruby", [
mockDbItem,
mockDbItem2,
]);
const databaseItem =
await SkeletonQueryWizard.findDatabaseItemByLanguage("ruby", [
mockDbItem,
mockDbItem2,
]);
expect(databaseItem).toEqual(mockDbItem);
});
it("should ignore databases with errors", async () => {
const mockDbItem = createMockDB(dir, {
language: "ruby",
} as FullDatabaseOptions);
const mockDbItem2 = createMockDB(dir, {
language: "javascript",
} as FullDatabaseOptions);
const mockDbItem3 = createMockDB(dir, {
language: "ruby",
} as FullDatabaseOptions);
jest
.spyOn(mockDbItem, "error", "get")
.mockReturnValue(asError("database go boom!"));
const databaseItem = await wizard.findDatabaseItemByLanguage("ruby", [
mockDbItem,
mockDbItem2,
mockDbItem3,
]);
expect(JSON.stringify(databaseItem)).toEqual(
JSON.stringify(mockDbItem3),
);
});
});
describe("when the item doesn't exist", () => {
@@ -424,13 +373,258 @@ describe("SkeletonQueryWizard", () => {
const mockDbItem = createMockDB(dir);
const mockDbItem2 = createMockDB(dir);
const databaseItem = await wizard.findDatabaseItemByLanguage("ruby", [
mockDbItem,
mockDbItem2,
]);
const databaseItem =
await SkeletonQueryWizard.findDatabaseItemByLanguage("ruby", [
mockDbItem,
mockDbItem2,
]);
expect(databaseItem).toBeUndefined();
});
});
});
describe("determineStoragePath", () => {
it("should prompt the user to provide a storage path", async () => {
const chosenPath = await wizard.determineStoragePath();
expect(showInputBoxSpy).toHaveBeenCalledWith(
expect.objectContaining({ value: storagePath }),
);
expect(chosenPath).toEqual(storagePath);
});
it("should write the chosen folder to settings", async () => {
const updateValueSpy = jest.spyOn(Setting.prototype, "updateValue");
await wizard.determineStoragePath();
expect(updateValueSpy).toHaveBeenCalledWith(storagePath, 1);
});
describe("when the user is using the codespace template", () => {
let originalValue: any;
let storedPath: string;
beforeEach(async () => {
storedPath = join(dir.name, "pickles-folder");
ensureDirSync(storedPath);
originalValue = workspace
.getConfiguration("codeQL.createQuery")
.get("folder");
// Set isCodespacesTemplate to true to indicate we are in the codespace template
await workspace
.getConfiguration("codeQL")
.update("codespacesTemplate", true);
});
afterEach(async () => {
await workspace
.getConfiguration("codeQL")
.update("codespacesTemplate", originalValue);
});
it("should not prompt the user", async () => {
const chosenPath = await wizard.determineStoragePath();
expect(showInputBoxSpy).not.toHaveBeenCalled();
expect(chosenPath).toEqual(storagePath);
});
});
describe("when there is already a saved storage path in settings", () => {
describe("when the saved storage path exists", () => {
let originalValue: any;
let storedPath: string;
beforeEach(async () => {
storedPath = join(dir.name, "pickles-folder");
ensureDirSync(storedPath);
originalValue = workspace
.getConfiguration("codeQL.createQuery")
.get("folder");
await workspace
.getConfiguration("codeQL.createQuery")
.update("folder", storedPath);
});
afterEach(async () => {
await workspace
.getConfiguration("codeQL.createQuery")
.update("folder", originalValue);
});
it("should return it and not prompt the user", async () => {
const chosenPath = await wizard.determineStoragePath();
expect(showInputBoxSpy).not.toHaveBeenCalled();
expect(chosenPath).toEqual(storedPath);
});
});
describe("when the saved storage path does not exist", () => {
let originalValue: any;
let storedPath: string;
beforeEach(async () => {
storedPath = join(dir.name, "this-folder-does-not-exist");
originalValue = workspace
.getConfiguration("codeQL.createQuery")
.get("folder");
await workspace
.getConfiguration("codeQL.createQuery")
.update("folder", storedPath);
});
afterEach(async () => {
await workspace
.getConfiguration("codeQL.createQuery")
.update("folder", originalValue);
});
it("should prompt the user for to provide a new folder name", async () => {
const chosenPath = await wizard.determineStoragePath();
expect(showInputBoxSpy).toHaveBeenCalled();
expect(chosenPath).toEqual(storagePath);
});
});
});
});
describe("sortDatabaseItemsByDateAdded", () => {
describe("should return a sorted list", () => {
it("should sort the items by dateAdded", async () => {
const mockDbItem = createMockDB(dir, {
dateAdded: 678,
} as FullDatabaseOptions);
const mockDbItem2 = createMockDB(dir, {
dateAdded: 123,
} as FullDatabaseOptions);
const mockDbItem3 = createMockDB(dir, {
dateAdded: undefined,
} as FullDatabaseOptions);
const mockDbItem4 = createMockDB(dir, {
dateAdded: 345,
} as FullDatabaseOptions);
const sortedList =
await SkeletonQueryWizard.sortDatabaseItemsByDateAdded([
mockDbItem,
mockDbItem2,
mockDbItem3,
mockDbItem4,
]);
expect(sortedList).toEqual([
mockDbItem3,
mockDbItem2,
mockDbItem4,
mockDbItem,
]);
});
it("should ignore databases with errors", async () => {
const mockDbItem = createMockDB(dir, {
dateAdded: 678,
} as FullDatabaseOptions);
const mockDbItem2 = createMockDB(dir, {
dateAdded: undefined,
} as FullDatabaseOptions);
const mockDbItem3 = createMockDB(dir, {
dateAdded: 345,
} as FullDatabaseOptions);
const mockDbItem4 = createMockDB(dir, {
dateAdded: 123,
} as FullDatabaseOptions);
jest
.spyOn(mockDbItem, "error", "get")
.mockReturnValue(asError("database go boom!"));
const sortedList =
await SkeletonQueryWizard.sortDatabaseItemsByDateAdded([
mockDbItem,
mockDbItem2,
mockDbItem3,
mockDbItem4,
]);
expect(sortedList).toEqual([mockDbItem2, mockDbItem4, mockDbItem3]);
});
});
});
describe("findExistingDatabaseItem", () => {
describe("when there are multiple items with the same name", () => {
it("should choose the latest one", async () => {
const mockDbItem = createMockDB(dir, {
language: "javascript",
dateAdded: 456,
} as FullDatabaseOptions);
const mockDbItem2 = createMockDB(dir, {
language: "ruby",
dateAdded: 789,
} as FullDatabaseOptions);
const mockDbItem3 = createMockDB(dir, {
language: "javascript",
dateAdded: 123,
} as FullDatabaseOptions);
const mockDbItem4 = createMockDB(dir, {
language: "javascript",
dateAdded: undefined,
} as FullDatabaseOptions);
jest
.spyOn(mockDbItem, "name", "get")
.mockReturnValue(QUERY_LANGUAGE_TO_DATABASE_REPO["javascript"]);
jest
.spyOn(mockDbItem2, "name", "get")
.mockReturnValue(QUERY_LANGUAGE_TO_DATABASE_REPO["javascript"]);
const databaseItem = await SkeletonQueryWizard.findExistingDatabaseItem(
"javascript",
[mockDbItem, mockDbItem2, mockDbItem3, mockDbItem4],
);
expect(JSON.stringify(databaseItem)).toEqual(
JSON.stringify(mockDbItem),
);
});
});
describe("when there are multiple items with the same language", () => {
it("should choose the latest one", async () => {
const mockDbItem = createMockDB(dir, {
language: "ruby",
dateAdded: 789,
} as FullDatabaseOptions);
const mockDbItem2 = createMockDB(dir, {
language: "javascript",
dateAdded: 456,
} as FullDatabaseOptions);
const mockDbItem3 = createMockDB(dir, {
language: "ruby",
dateAdded: 123,
} as FullDatabaseOptions);
const mockDbItem4 = createMockDB(dir, {
language: "javascript",
dateAdded: undefined,
} as FullDatabaseOptions);
const databaseItem = await SkeletonQueryWizard.findExistingDatabaseItem(
"javascript",
[mockDbItem, mockDbItem2, mockDbItem3, mockDbItem4],
);
expect(JSON.stringify(databaseItem)).toEqual(
JSON.stringify(mockDbItem2),
);
});
});
});
});

View File

@@ -20,7 +20,7 @@ import {
encodeSourceArchiveUri,
} from "../../../src/archive-filesystem-provider";
import { testDisposeHandler } from "../test-dispose-handler";
import { QueryRunner } from "../../../src/queryRunner";
import { QueryRunner } from "../../../src/query-server/query-runner";
import * as helpers from "../../../src/helpers";
import { Setting } from "../../../src/config";
import { QlPackGenerator } from "../../../src/qlpack-generator";

View File

@@ -6,10 +6,8 @@ import { dir } from "tmp-promise";
import { QlpacksInfo, ResolveExtensionsResult } from "../../../../src/cli";
import * as helpers from "../../../../src/helpers";
import {
ExtensionPack,
pickExtensionPackModelFile,
} from "../../../../src/data-extensions-editor/extension-pack-picker";
import { pickExtensionPackModelFile } from "../../../../src/data-extensions-editor/extension-pack-picker";
import { ExtensionPack } from "../../../../src/data-extensions-editor/shared/extension-pack";
describe("pickExtensionPackModelFile", () => {
let tmpDir: string;

View File

@@ -635,7 +635,7 @@ describe("prepareCodeTour", () => {
describe("if the workspace is already open", () => {
it("should not open the tutorial workspace", async () => {
// Set isCodespaceTemplate to true to indicate the workspace has already been opened
// Set isCodespacesTemplate to true to indicate the workspace has already been opened
jest.spyOn(Setting.prototype, "getValue").mockReturnValue(true);
// set up directory to have a 'tutorial.code-workspace' file

View File

@@ -9,7 +9,7 @@ import { tmpDir } from "../../../../src/helpers";
import { HistoryItemLabelProvider } from "../../../../src/query-history/history-item-label-provider";
import { ResultsView } from "../../../../src/interface";
import { EvalLogViewer } from "../../../../src/eval-log-viewer";
import { QueryRunner } from "../../../../src/queryRunner";
import { QueryRunner } from "../../../../src/query-server/query-runner";
import { VariantAnalysisManager } from "../../../../src/variant-analysis/variant-analysis-manager";
import { QueryHistoryInfo } from "../../../../src/query-history/query-history-info";
import {

View File

@@ -10,7 +10,7 @@ import { tmpDir } from "../../../../src/helpers";
import { HistoryItemLabelProvider } from "../../../../src/query-history/history-item-label-provider";
import { ResultsView } from "../../../../src/interface";
import { EvalLogViewer } from "../../../../src/eval-log-viewer";
import { QueryRunner } from "../../../../src/queryRunner";
import { QueryRunner } from "../../../../src/query-server/query-runner";
import { VariantAnalysisManager } from "../../../../src/variant-analysis/variant-analysis-manager";
import { QueryHistoryInfo } from "../../../../src/query-history/query-history-info";
import {

View File

@@ -13,7 +13,7 @@ import { DatabaseInfo } from "../../../../../src/pure/interface-types";
import { CancellationTokenSource, Uri } from "vscode";
import { tmpDir } from "../../../../../src/helpers";
import { QueryResultType } from "../../../../../src/pure/legacy-messages";
import { QueryInProgress } from "../../../../../src/legacy-query-server/run-queries";
import { QueryInProgress } from "../../../../../src/query-server/legacy";
import { VariantAnalysisHistoryItem } from "../../../../../src/query-history/variant-analysis-history-item";
import { QueryHistoryInfo } from "../../../../../src/query-history/query-history-info";
import { createMockVariantAnalysisHistoryItem } from "../../../../factories/query-history/variant-analysis-history-item";

View File

@@ -16,7 +16,7 @@ import { testDisposeHandler } from "../../test-dispose-handler";
import { HistoryItemLabelProvider } from "../../../../src/query-history/history-item-label-provider";
import { ResultsView } from "../../../../src/interface";
import { EvalLogViewer } from "../../../../src/eval-log-viewer";
import { QueryRunner } from "../../../../src/queryRunner";
import { QueryRunner } from "../../../../src/query-server/query-runner";
import { VariantAnalysisManager } from "../../../../src/variant-analysis/variant-analysis-manager";
import { QueryHistoryManager } from "../../../../src/query-history/query-history-manager";
import { mockedObject } from "../../utils/mocking.helpers";

View File

@@ -24,7 +24,7 @@ import { tmpDir } from "../../../src/helpers";
import {
formatLegacyMessage,
QueryInProgress,
} from "../../../src/legacy-query-server/run-queries";
} from "../../../src/query-server/legacy/run-queries";
import {
EvaluationResult,
QueryResultType,

View File

@@ -10,14 +10,16 @@ import {
} from "../../../src/pure/legacy-messages";
import * as config from "../../../src/config";
import { tmpDir } from "../../../src/helpers";
import { QueryServerClient } from "../../../src/legacy-query-server/queryserver-client";
import { CodeQLCliServer } from "../../../src/cli";
import { SELECT_QUERY_NAME } from "../../../src/contextual/locationFinder";
import {
QueryInProgress,
compileQuery as compileQueryLegacy,
} from "../../../src/legacy-query-server/run-queries";
import { LegacyQueryRunner } from "../../../src/legacy-query-server/legacyRunner";
} from "../../../src/query-server/legacy/run-queries";
import {
LegacyQueryRunner,
QueryServerClient,
} from "../../../src/query-server/legacy";
import { DatabaseItem } from "../../../src/local-databases";
import { DeepPartial, mockedObject } from "../utils/mocking.helpers";
import { BqrsKind } from "../../../src/pure/bqrs-cli-types";