Skip to content

Commit 0f3b36c

Browse files
committed
Resolve queries instead of hardcoding the path
This will change the data extensions editor generator to resolve the queries based on the `modelgenerator` tag. This removes the requirement of having a `ql` folder in the workspace. I chose to use the path instead of the `id` for now to avoid having to resolve the query 4 times. This also avoids the need to map the language names to the language ID in the tag (i.e. `csharp` -> `cs`).
1 parent 3e6372d commit 0f3b36c

File tree

2 files changed

+66
-38
lines changed

2 files changed

+66
-38
lines changed

extensions/ql-vscode/src/data-extensions-editor/data-extensions-editor-view.ts

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
ViewColumn,
66
window,
77
workspace,
8-
WorkspaceFolder,
98
} from "vscode";
109
import {
1110
AbstractWebview,
@@ -40,19 +39,6 @@ import { ExternalApiUsage } from "./external-api-usage";
4039
import { ModeledMethod } from "./modeled-method";
4140
import { ExtensionPackModelFile } from "./shared/extension-pack";
4241

43-
function getQlSubmoduleFolder(): WorkspaceFolder | undefined {
44-
const workspaceFolder = workspace.workspaceFolders?.find(
45-
(folder) => folder.name === "ql",
46-
);
47-
if (!workspaceFolder) {
48-
void extLogger.log("No workspace folder 'ql' found");
49-
50-
return;
51-
}
52-
53-
return workspaceFolder;
54-
}
55-
5642
export class DataExtensionsEditorView extends AbstractWebview<
5743
ToDataExtensionsEditorMessage,
5844
FromDataExtensionsEditorMessage
@@ -309,11 +295,6 @@ export class DataExtensionsEditorView extends AbstractWebview<
309295
// but we need to set it back to the originally selected database.
310296
await this.databaseManager.setCurrentDatabaseItem(selectedDatabase);
311297

312-
const workspaceFolder = getQlSubmoduleFolder();
313-
if (!workspaceFolder) {
314-
return;
315-
}
316-
317298
await this.showProgress({
318299
step: 0,
319300
maxStep: 4000,
@@ -325,7 +306,6 @@ export class DataExtensionsEditorView extends AbstractWebview<
325306
cliServer: this.cliServer,
326307
queryRunner: this.queryRunner,
327308
queryStorageDir: this.queryStorageDir,
328-
qlDir: workspaceFolder.uri.fsPath,
329309
databaseItem: database,
330310
onResults: async (results) => {
331311
const modeledMethodsByName: Record<string, ModeledMethod> = {};

extensions/ql-vscode/src/data-extensions-editor/generate-flow-model.ts

Lines changed: 66 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { CancellationToken } from "vscode";
22
import { DatabaseItem } from "../databases/local-databases";
3-
import { join } from "path";
3+
import { basename } from "path";
44
import { QueryRunner } from "../query-server";
55
import { CodeQLCliServer } from "../codeql-cli/cli";
66
import { TeeLogger } from "../common";
@@ -16,44 +16,83 @@ import {
1616
} from "./modeled-method";
1717
import { redactableError } from "../pure/errors";
1818
import { QueryResultType } from "../pure/new-messages";
19+
import { file } from "tmp-promise";
20+
import { writeFile } from "fs-extra";
21+
import { dump } from "js-yaml";
22+
import { qlpackOfDatabase } from "../language-support";
1923

2024
type FlowModelOptions = {
2125
cliServer: CodeQLCliServer;
2226
queryRunner: QueryRunner;
2327
queryStorageDir: string;
24-
qlDir: string;
2528
databaseItem: DatabaseItem;
2629
progress: ProgressCallback;
2730
token: CancellationToken;
2831
onResults: (results: ModeledMethodWithSignature[]) => void | Promise<void>;
2932
};
3033

34+
export async function resolveQueries(
35+
cliServer: CodeQLCliServer,
36+
databaseItem: DatabaseItem,
37+
): Promise<string[]> {
38+
const qlpacks = await qlpackOfDatabase(cliServer, databaseItem);
39+
40+
const packsToSearch: string[] = [];
41+
42+
// The CLI can handle both library packs and query packs, so search both packs in order.
43+
packsToSearch.push(qlpacks.dbschemePack);
44+
if (qlpacks.queryPack !== undefined) {
45+
packsToSearch.push(qlpacks.queryPack);
46+
}
47+
48+
const suiteFile = (
49+
await file({
50+
postfix: ".qls",
51+
})
52+
).path;
53+
const suiteYaml = [];
54+
for (const qlpack of packsToSearch) {
55+
suiteYaml.push({
56+
from: qlpack,
57+
queries: ".",
58+
include: {
59+
"tags contain": "modelgenerator",
60+
},
61+
});
62+
}
63+
await writeFile(suiteFile, dump(suiteYaml), "utf8");
64+
65+
return await cliServer.resolveQueriesInSuite(
66+
suiteFile,
67+
getOnDiskWorkspaceFolders(),
68+
);
69+
}
70+
3171
async function getModeledMethodsFromFlow(
3272
type: Exclude<ModeledMethodType, "none">,
33-
queryName: string,
73+
queryPath: string | undefined,
3474
queryStep: number,
3575
{
3676
cliServer,
3777
queryRunner,
3878
queryStorageDir,
39-
qlDir,
4079
databaseItem,
4180
progress,
4281
token,
4382
}: Omit<FlowModelOptions, "onResults">,
4483
): Promise<ModeledMethodWithSignature[]> {
45-
const definition = extensiblePredicateDefinitions[type];
84+
if (queryPath === undefined) {
85+
void showAndLogExceptionWithTelemetry(
86+
redactableError`Failed to find ${type} query`,
87+
);
88+
return [];
89+
}
4690

47-
const query = join(
48-
qlDir,
49-
databaseItem.language,
50-
"ql/src/utils/modelgenerator",
51-
queryName,
52-
);
91+
const definition = extensiblePredicateDefinitions[type];
5392

5493
const queryRun = queryRunner.createQueryRun(
5594
databaseItem.databaseUri.fsPath,
56-
{ queryPath: query, quickEvalPosition: undefined },
95+
{ queryPath, quickEvalPosition: undefined },
5796
false,
5897
getOnDiskWorkspaceFolders(),
5998
undefined,
@@ -74,7 +113,7 @@ async function getModeledMethodsFromFlow(
74113
);
75114
if (queryResult.resultType !== QueryResultType.SUCCESS) {
76115
void showAndLogExceptionWithTelemetry(
77-
redactableError`Failed to run ${queryName} query: ${
116+
redactableError`Failed to run ${basename(queryPath)} query: ${
78117
queryResult.message ?? "No message"
79118
}`,
80119
);
@@ -86,7 +125,9 @@ async function getModeledMethodsFromFlow(
86125
const bqrsInfo = await cliServer.bqrsInfo(bqrsPath);
87126
if (bqrsInfo["result-sets"].length !== 1) {
88127
void showAndLogExceptionWithTelemetry(
89-
redactableError`Expected exactly one result set, got ${bqrsInfo["result-sets"].length} for ${queryName}`,
128+
redactableError`Expected exactly one result set, got ${
129+
bqrsInfo["result-sets"].length
130+
} for ${basename(queryPath)}`,
90131
);
91132
}
92133

@@ -112,9 +153,16 @@ export async function generateFlowModel({
112153
onResults,
113154
...options
114155
}: FlowModelOptions) {
156+
const queries = await resolveQueries(options.cliServer, options.databaseItem);
157+
158+
const queriesByBasename: Record<string, string> = {};
159+
for (const query of queries) {
160+
queriesByBasename[basename(query)] = query;
161+
}
162+
115163
const summaryResults = await getModeledMethodsFromFlow(
116164
"summary",
117-
"CaptureSummaryModels.ql",
165+
queriesByBasename["CaptureSummaryModels.ql"],
118166
0,
119167
options,
120168
);
@@ -124,7 +172,7 @@ export async function generateFlowModel({
124172

125173
const sinkResults = await getModeledMethodsFromFlow(
126174
"sink",
127-
"CaptureSinkModels.ql",
175+
queriesByBasename["CaptureSinkModels.ql"],
128176
1,
129177
options,
130178
);
@@ -134,7 +182,7 @@ export async function generateFlowModel({
134182

135183
const sourceResults = await getModeledMethodsFromFlow(
136184
"source",
137-
"CaptureSourceModels.ql",
185+
queriesByBasename["CaptureSourceModels.ql"],
138186
2,
139187
options,
140188
);
@@ -144,7 +192,7 @@ export async function generateFlowModel({
144192

145193
const neutralResults = await getModeledMethodsFromFlow(
146194
"neutral",
147-
"CaptureNeutralModels.ql",
195+
queriesByBasename["CaptureNeutralModels.ql"],
148196
3,
149197
options,
150198
);

0 commit comments

Comments
 (0)