Merge pull request #2742 from github/starcke/flow-queries-refactor

Starcke/flow queries refactor
This commit is contained in:
Anders Starcke Henriksen
2023-08-24 13:38:13 +02:00
committed by GitHub
2 changed files with 82 additions and 120 deletions

View File

@@ -22,7 +22,7 @@ import {
import { DatabaseItem, DatabaseManager } from "../databases/local-databases"; import { DatabaseItem, DatabaseManager } from "../databases/local-databases";
import { CodeQLCliServer } from "../codeql-cli/cli"; import { CodeQLCliServer } from "../codeql-cli/cli";
import { asError, assertNever, getErrorMessage } from "../common/helpers-pure"; import { asError, assertNever, getErrorMessage } from "../common/helpers-pure";
import { generateFlowModel } from "./flow-model-queries"; import { runFlowModelQueries } from "./flow-model-queries";
import { promptImportGithubDatabase } from "../databases/database-fetcher"; import { promptImportGithubDatabase } from "../databases/database-fetcher";
import { App } from "../common/app"; import { App } from "../common/app";
import { showResolvableLocation } from "../databases/local-databases/locations"; import { showResolvableLocation } from "../databases/local-databases/locations";
@@ -389,7 +389,7 @@ export class DataExtensionsEditorView extends AbstractWebview<
}); });
try { try {
await generateFlowModel({ await runFlowModelQueries({
cliServer: this.cliServer, cliServer: this.cliServer,
queryRunner: this.queryRunner, queryRunner: this.queryRunner,
queryStorageDir: this.queryStorageDir, queryStorageDir: this.queryStorageDir,

View File

@@ -3,19 +3,16 @@ import { DatabaseItem } from "../databases/local-databases";
import { basename } from "path"; import { basename } from "path";
import { QueryRunner } from "../query-server"; import { QueryRunner } from "../query-server";
import { CodeQLCliServer } from "../codeql-cli/cli"; import { CodeQLCliServer } from "../codeql-cli/cli";
import { showAndLogExceptionWithTelemetry, TeeLogger } from "../common/logging"; import { showAndLogExceptionWithTelemetry } from "../common/logging";
import { extLogger } from "../common/logging/vscode"; import { extLogger } from "../common/logging/vscode";
import { extensiblePredicateDefinitions } from "./predicates"; import { extensiblePredicateDefinitions } from "./predicates";
import { ProgressCallback } from "../common/vscode/progress"; import { ProgressCallback } from "../common/vscode/progress";
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders"; import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
import { ModeledMethod, ModeledMethodType } from "./modeled-method"; import { ModeledMethod, ModeledMethodType } from "./modeled-method";
import { redactableError } from "../common/errors"; import { redactableError } from "../common/errors";
import { QueryResultType } from "../query-server/new-messages"; import { qlpackOfDatabase, resolveQueries } from "../local-queries";
import { file } from "tmp-promise";
import { writeFile } from "fs-extra";
import { dump } from "js-yaml";
import { qlpackOfDatabase } from "../local-queries";
import { telemetryListener } from "../common/vscode/telemetry"; import { telemetryListener } from "../common/vscode/telemetry";
import { runQuery } from "../local-queries/run-query";
type FlowModelOptions = { type FlowModelOptions = {
cliServer: CodeQLCliServer; cliServer: CodeQLCliServer;
@@ -27,44 +24,73 @@ type FlowModelOptions = {
onResults: (results: ModeledMethod[]) => void | Promise<void>; onResults: (results: ModeledMethod[]) => void | Promise<void>;
}; };
async function resolveQueries( export async function runFlowModelQueries({
onResults,
...options
}: FlowModelOptions) {
const queries = await resolveFlowQueries(
options.cliServer,
options.databaseItem,
);
const queriesByBasename: Record<string, string> = {};
for (const query of queries) {
queriesByBasename[basename(query)] = query;
}
const summaryResults = await runSingleFlowQuery(
"summary",
queriesByBasename["CaptureSummaryModels.ql"],
0,
options,
);
if (summaryResults) {
await onResults(summaryResults);
}
const sinkResults = await runSingleFlowQuery(
"sink",
queriesByBasename["CaptureSinkModels.ql"],
1,
options,
);
if (sinkResults) {
await onResults(sinkResults);
}
const sourceResults = await runSingleFlowQuery(
"source",
queriesByBasename["CaptureSourceModels.ql"],
2,
options,
);
if (sourceResults) {
await onResults(sourceResults);
}
const neutralResults = await runSingleFlowQuery(
"neutral",
queriesByBasename["CaptureNeutralModels.ql"],
3,
options,
);
if (neutralResults) {
await onResults(neutralResults);
}
}
async function resolveFlowQueries(
cliServer: CodeQLCliServer, cliServer: CodeQLCliServer,
databaseItem: DatabaseItem, databaseItem: DatabaseItem,
): Promise<string[]> { ): Promise<string[]> {
const qlpacks = await qlpackOfDatabase(cliServer, databaseItem); const qlpacks = await qlpackOfDatabase(cliServer, databaseItem);
const packsToSearch: string[] = []; return await resolveQueries(cliServer, qlpacks, "flow model generator", {
"tags contain": ["modelgenerator"],
// The CLI can handle both library packs and query packs, so search both packs in order. });
packsToSearch.push(qlpacks.dbschemePack);
if (qlpacks.queryPack !== undefined) {
packsToSearch.push(qlpacks.queryPack);
}
const suiteFile = (
await file({
postfix: ".qls",
})
).path;
const suiteYaml = [];
for (const qlpack of packsToSearch) {
suiteYaml.push({
from: qlpack,
queries: ".",
include: {
"tags contain": "modelgenerator",
},
});
}
await writeFile(suiteFile, dump(suiteYaml), "utf8");
return await cliServer.resolveQueriesInSuite(
suiteFile,
getOnDiskWorkspaceFolders(),
);
} }
async function getModeledMethodsFromFlow( async function runSingleFlowQuery(
type: Exclude<ModeledMethodType, "none">, type: Exclude<ModeledMethodType, "none">,
queryPath: string | undefined, queryPath: string | undefined,
queryStep: number, queryStep: number,
@@ -77,6 +103,7 @@ async function getModeledMethodsFromFlow(
token, token,
}: Omit<FlowModelOptions, "onResults">, }: Omit<FlowModelOptions, "onResults">,
): Promise<ModeledMethod[]> { ): Promise<ModeledMethod[]> {
// Check that the right query was found
if (queryPath === undefined) { if (queryPath === undefined) {
void showAndLogExceptionWithTelemetry( void showAndLogExceptionWithTelemetry(
extLogger, extLogger,
@@ -86,45 +113,32 @@ async function getModeledMethodsFromFlow(
return []; return [];
} }
const definition = extensiblePredicateDefinitions[type]; // Run the query
const completedQuery = await runQuery({
const queryRun = queryRunner.createQueryRun( cliServer,
databaseItem.databaseUri.fsPath, queryRunner,
{ databaseItem,
queryPath, queryPath,
quickEvalPosition: undefined,
quickEvalCountOnly: false,
},
false,
getOnDiskWorkspaceFolders(),
undefined,
queryStorageDir, queryStorageDir,
undefined, additionalPacks: getOnDiskWorkspaceFolders(),
undefined, extensionPacks: undefined,
); progress: ({ step, message }) =>
const queryResult = await queryRun.evaluate(
({ step, message }) =>
progress({ progress({
message: `Generating ${type} model: ${message}`, message: `Generating ${type} model: ${message}`,
step: queryStep * 1000 + step, step: queryStep * 1000 + step,
maxStep: 4000, maxStep: 4000,
}), }),
token, token,
new TeeLogger(queryRunner.logger, queryRun.outputDir.logPath), });
);
if (queryResult.resultType !== QueryResultType.SUCCESS) { if (!completedQuery) {
void showAndLogExceptionWithTelemetry(
extLogger,
telemetryListener,
redactableError`Failed to run ${basename(queryPath)} query: ${
queryResult.message ?? "No message"
}`,
);
return []; return [];
} }
const bqrsPath = queryResult.outputDir.bqrsPath; // Interpret the results
const definition = extensiblePredicateDefinitions[type];
const bqrsPath = completedQuery.outputDir.bqrsPath;
const bqrsInfo = await cliServer.bqrsInfo(bqrsPath); const bqrsInfo = await cliServer.bqrsInfo(bqrsPath);
if (bqrsInfo["result-sets"].length !== 1) { if (bqrsInfo["result-sets"].length !== 1) {
@@ -154,55 +168,3 @@ async function getModeledMethodsFromFlow(
}) })
); );
} }
export async function generateFlowModel({
onResults,
...options
}: FlowModelOptions) {
const queries = await resolveQueries(options.cliServer, options.databaseItem);
const queriesByBasename: Record<string, string> = {};
for (const query of queries) {
queriesByBasename[basename(query)] = query;
}
const summaryResults = await getModeledMethodsFromFlow(
"summary",
queriesByBasename["CaptureSummaryModels.ql"],
0,
options,
);
if (summaryResults) {
await onResults(summaryResults);
}
const sinkResults = await getModeledMethodsFromFlow(
"sink",
queriesByBasename["CaptureSinkModels.ql"],
1,
options,
);
if (sinkResults) {
await onResults(sinkResults);
}
const sourceResults = await getModeledMethodsFromFlow(
"source",
queriesByBasename["CaptureSourceModels.ql"],
2,
options,
);
if (sourceResults) {
await onResults(sourceResults);
}
const neutralResults = await getModeledMethodsFromFlow(
"neutral",
queriesByBasename["CaptureNeutralModels.ql"],
3,
options,
);
if (neutralResults) {
await onResults(neutralResults);
}
}