Use functions instead of class for generating flow model

We were using a single-use class for generating the flow model, while we
are actually able to do it using two functions. This is more in line
with our existing codebase.
This commit is contained in:
Koen Vlaswinkel
2023-04-11 10:27:46 +02:00
parent 4e8df309fb
commit 102976e167
2 changed files with 127 additions and 137 deletions

View File

@@ -276,13 +276,13 @@ export class DataExtensionsEditorView extends AbstractWebview<
});
try {
await generateFlowModel(
this.cliServer,
this.queryRunner,
this.queryStorageDir,
workspaceFolder.uri.fsPath,
database,
async (results) => {
await generateFlowModel({
cliServer: this.cliServer,
queryRunner: this.queryRunner,
queryStorageDir: this.queryStorageDir,
qlDir: workspaceFolder.uri.fsPath,
databaseItem: database,
onResults: async (results) => {
const modeledMethodsByName: Record<string, ModeledMethod> = {};
for (const result of results) {
@@ -295,9 +295,9 @@ export class DataExtensionsEditorView extends AbstractWebview<
overrideNone: true,
});
},
(update) => this.showProgress(update),
tokenSource.token,
);
progress: (update) => this.showProgress(update),
token: tokenSource.token,
});
} catch (e: unknown) {
void showAndLogExceptionWithTelemetry(
redactableError(

View File

@@ -12,140 +12,130 @@ import {
ModeledMethodWithSignature,
} from "./modeled-method";
class FlowModelGenerator {
constructor(
private readonly cli: CodeQLCliServer,
private readonly queryRunner: QueryRunner,
private readonly queryStorageDir: string,
private readonly qlDir: string,
private readonly databaseItem: DatabaseItem,
private readonly progress: ProgressCallback,
private readonly token: CancellationToken,
) {}
type FlowModelOptions = {
cliServer: CodeQLCliServer;
queryRunner: QueryRunner;
queryStorageDir: string;
qlDir: string;
databaseItem: DatabaseItem;
progress: ProgressCallback;
token: CancellationToken;
onResults: (results: ModeledMethodWithSignature[]) => void | Promise<void>;
};
private async getModeledMethodsFromFlow(
type: Exclude<ModeledMethodType, "none">,
queryName: string,
queryStep: number,
): Promise<ModeledMethodWithSignature[]> {
const definition = extensiblePredicateDefinitions[type];
const query = join(
this.qlDir,
this.databaseItem.language,
"ql/src/utils/modelgenerator",
queryName,
);
const queryRun = this.queryRunner.createQueryRun(
this.databaseItem.databaseUri.fsPath,
{ queryPath: query, quickEvalPosition: undefined },
false,
getOnDiskWorkspaceFolders(),
undefined,
this.queryStorageDir,
undefined,
undefined,
);
const queryResult = await queryRun.evaluate(
({ step, message }) =>
this.progress({
message: `Generating ${type} model: ${message}`,
step: queryStep * 1000 + step,
maxStep: 4000,
}),
this.token,
new TeeLogger(this.queryRunner.logger, queryRun.outputDir.logPath),
);
const bqrsPath = queryResult.outputDir.bqrsPath;
const bqrsInfo = await this.cli.bqrsInfo(bqrsPath);
if (bqrsInfo["result-sets"].length !== 1) {
throw new Error(
`Expected exactly one result set, got ${bqrsInfo["result-sets"].length}`,
);
}
const resultSet = bqrsInfo["result-sets"][0];
const decodedResults = await this.cli.bqrsDecode(bqrsPath, resultSet.name);
const results = decodedResults.tuples;
return (
results
// This is just a sanity check. The query should only return strings.
.filter((result) => typeof result[0] === "string")
.map((result) => {
const row = result[0] as string;
return definition.readModeledMethod(row.split(";"));
})
);
}
async run(
onResults: (results: ModeledMethodWithSignature[]) => void | Promise<void>,
) {
const summaryResults = await this.getModeledMethodsFromFlow(
"summary",
"CaptureSummaryModels.ql",
0,
);
if (summaryResults) {
await onResults(summaryResults);
}
const sinkResults = await this.getModeledMethodsFromFlow(
"sink",
"CaptureSinkModels.ql",
1,
);
if (sinkResults) {
await onResults(sinkResults);
}
const sourceResults = await this.getModeledMethodsFromFlow(
"source",
"CaptureSourceModels.ql",
2,
);
if (sourceResults) {
await onResults(sourceResults);
}
const neutralResults = await this.getModeledMethodsFromFlow(
"neutral",
"CaptureNeutralModels.ql",
3,
);
if (neutralResults) {
await onResults(neutralResults);
}
}
}
export async function generateFlowModel(
cli: CodeQLCliServer,
queryRunner: QueryRunner,
queryStorageDir: string,
qlDir: string,
databaseItem: DatabaseItem,
onResults: (results: ModeledMethodWithSignature[]) => void | Promise<void>,
progress: ProgressCallback,
token: CancellationToken,
) {
const generator = new FlowModelGenerator(
cli,
async function getModeledMethodsFromFlow(
type: Exclude<ModeledMethodType, "none">,
queryName: string,
queryStep: number,
{
cliServer,
queryRunner,
queryStorageDir,
qlDir,
databaseItem,
progress,
token,
}: Omit<FlowModelOptions, "onResults">,
): Promise<ModeledMethodWithSignature[]> {
const definition = extensiblePredicateDefinitions[type];
const query = join(
qlDir,
databaseItem.language,
"ql/src/utils/modelgenerator",
queryName,
);
return generator.run(onResults);
const queryRun = queryRunner.createQueryRun(
databaseItem.databaseUri.fsPath,
{ queryPath: query, quickEvalPosition: undefined },
false,
getOnDiskWorkspaceFolders(),
undefined,
queryStorageDir,
undefined,
undefined,
);
const queryResult = await queryRun.evaluate(
({ step, message }) =>
progress({
message: `Generating ${type} model: ${message}`,
step: queryStep * 1000 + step,
maxStep: 4000,
}),
token,
new TeeLogger(queryRunner.logger, queryRun.outputDir.logPath),
);
const bqrsPath = queryResult.outputDir.bqrsPath;
const bqrsInfo = await cliServer.bqrsInfo(bqrsPath);
if (bqrsInfo["result-sets"].length !== 1) {
throw new Error(
`Expected exactly one result set, got ${bqrsInfo["result-sets"].length}`,
);
}
const resultSet = bqrsInfo["result-sets"][0];
const decodedResults = await cliServer.bqrsDecode(bqrsPath, resultSet.name);
const results = decodedResults.tuples;
return (
results
// This is just a sanity check. The query should only return strings.
.filter((result) => typeof result[0] === "string")
.map((result) => {
const row = result[0] as string;
return definition.readModeledMethod(row.split(";"));
})
);
}
export async function generateFlowModel({
onResults,
...options
}: FlowModelOptions) {
const summaryResults = await getModeledMethodsFromFlow(
"summary",
"CaptureSummaryModels.ql",
0,
options,
);
if (summaryResults) {
await onResults(summaryResults);
}
const sinkResults = await getModeledMethodsFromFlow(
"sink",
"CaptureSinkModels.ql",
1,
options,
);
if (sinkResults) {
await onResults(sinkResults);
}
const sourceResults = await getModeledMethodsFromFlow(
"source",
"CaptureSourceModels.ql",
2,
options,
);
if (sourceResults) {
await onResults(sourceResults);
}
const neutralResults = await getModeledMethodsFromFlow(
"neutral",
"CaptureNeutralModels.ql",
3,
options,
);
if (neutralResults) {
await onResults(neutralResults);
}
}