Skip to content

Commit a76bd46

Browse files
authored
Merge pull request #2535 from github/koesie10/framework-mode
Add initial implementation of framework mode
2 parents 82cdf03 + 4f36711 commit a76bd46

File tree

20 files changed

+655
-108
lines changed

20 files changed

+655
-108
lines changed

extensions/ql-vscode/src/common/interface-types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { DataFlowPaths } from "../variant-analysis/shared/data-flow-paths";
2020
import { ExternalApiUsage } from "../data-extensions-editor/external-api-usage";
2121
import { ModeledMethod } from "../data-extensions-editor/modeled-method";
2222
import { DataExtensionEditorViewState } from "../data-extensions-editor/shared/view-state";
23+
import { Mode } from "../data-extensions-editor/shared/mode";
2324

2425
/**
2526
* This module contains types and code that are shared between
@@ -521,6 +522,11 @@ export interface AddModeledMethodsMessage {
521522
overrideNone?: boolean;
522523
}
523524

525+
export interface SwitchModeMessage {
526+
t: "switchMode";
527+
mode: Mode;
528+
}
529+
524530
export interface JumpToUsageMessage {
525531
t: "jumpToUsage";
526532
location: ResolvableLocationValue;
@@ -559,6 +565,7 @@ export type ToDataExtensionsEditorMessage =
559565

560566
export type FromDataExtensionsEditorMessage =
561567
| ViewLoadedMsg
568+
| SwitchModeMessage
562569
| OpenModelFileMessage
563570
| OpenExtensionPackMessage
564571
| JumpToUsageMessage

extensions/ql-vscode/src/config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,7 @@ export function showQueriesPanel(): boolean {
714714

715715
const DATA_EXTENSIONS = new Setting("dataExtensions", ROOT_SETTING);
716716
const LLM_GENERATION = new Setting("llmGeneration", DATA_EXTENSIONS);
717+
const FRAMEWORK_MODE = new Setting("frameworkMode", DATA_EXTENSIONS);
717718
const DISABLE_AUTO_NAME_EXTENSION_PACK = new Setting(
718719
"disableAutoNameExtensionPack",
719720
DATA_EXTENSIONS,
@@ -723,6 +724,10 @@ export function showLlmGeneration(): boolean {
723724
return !!LLM_GENERATION.getValue<boolean>();
724725
}
725726

727+
export function enableFrameworkMode(): boolean {
728+
return !!FRAMEWORK_MODE.getValue<boolean>();
729+
}
730+
726731
export function disableAutoNameExtensionPack(): boolean {
727732
return !!DISABLE_AUTO_NAME_EXTENSION_PACK.getValue<boolean>();
728733
}

extensions/ql-vscode/src/data-extensions-editor/auto-model-usages-query.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export async function getAutoModelUsages({
3232
// This will re-run the query that was already run when opening the data extensions editor. This
3333
// might be unnecessary, but this makes it really easy to get the path to the BQRS file which we
3434
// need to interpret the results.
35-
const queryResult = await runQuery({
35+
const queryResult = await runQuery("applicationModeQuery", {
3636
cliServer,
3737
queryRunner,
3838
queryStorageDir,

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

Lines changed: 85 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ import { decodeBqrsToExternalApiUsages } from "./bqrs";
3636
import { redactableError } from "../common/errors";
3737
import { readQueryResults, runQuery } from "./external-api-usage-query";
3838
import {
39-
createDataExtensionYamlsPerLibrary,
39+
createDataExtensionYamlsForApplicationMode,
40+
createDataExtensionYamlsForFrameworkMode,
4041
createFilenameForLibrary,
4142
loadDataExtensionYaml,
4243
} from "./yaml";
@@ -48,9 +49,10 @@ import {
4849
createAutoModelRequest,
4950
parsePredictedClassifications,
5051
} from "./auto-model";
51-
import { showLlmGeneration } from "../config";
52+
import { enableFrameworkMode, showLlmGeneration } from "../config";
5253
import { getAutoModelUsages } from "./auto-model-usages-query";
5354
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
55+
import { Mode } from "./shared/mode";
5456

5557
export class DataExtensionsEditorView extends AbstractWebview<
5658
ToDataExtensionsEditorMessage,
@@ -65,6 +67,7 @@ export class DataExtensionsEditorView extends AbstractWebview<
6567
private readonly queryStorageDir: string,
6668
private readonly databaseItem: DatabaseItem,
6769
private readonly extensionPack: ExtensionPack,
70+
private mode: Mode = Mode.Application,
6871
) {
6972
super(ctx);
7073
}
@@ -138,6 +141,12 @@ export class DataExtensionsEditorView extends AbstractWebview<
138141
msg.modeledMethods,
139142
);
140143

144+
break;
145+
case "switchMode":
146+
this.mode = msg.mode;
147+
148+
await Promise.all([this.setViewState(), this.loadExternalApiUsages()]);
149+
141150
break;
142151
default:
143152
assertNever(msg);
@@ -159,7 +168,9 @@ export class DataExtensionsEditorView extends AbstractWebview<
159168
t: "setDataExtensionEditorViewState",
160169
viewState: {
161170
extensionPack: this.extensionPack,
171+
enableFrameworkMode: enableFrameworkMode(),
162172
showLlmButton: showLlmGeneration(),
173+
mode: this.mode,
163174
},
164175
});
165176
}
@@ -188,11 +199,26 @@ export class DataExtensionsEditorView extends AbstractWebview<
188199
externalApiUsages: ExternalApiUsage[],
189200
modeledMethods: Record<string, ModeledMethod>,
190201
): Promise<void> {
191-
const yamls = createDataExtensionYamlsPerLibrary(
192-
this.databaseItem.language,
193-
externalApiUsages,
194-
modeledMethods,
195-
);
202+
let yamls: Record<string, string>;
203+
switch (this.mode) {
204+
case Mode.Application:
205+
yamls = createDataExtensionYamlsForApplicationMode(
206+
this.databaseItem.language,
207+
externalApiUsages,
208+
modeledMethods,
209+
);
210+
break;
211+
case Mode.Framework:
212+
yamls = createDataExtensionYamlsForFrameworkMode(
213+
this.databaseItem.name,
214+
this.databaseItem.language,
215+
externalApiUsages,
216+
modeledMethods,
217+
);
218+
break;
219+
default:
220+
assertNever(this.mode);
221+
}
196222

197223
for (const [filename, yaml] of Object.entries(yamls)) {
198224
await outputFile(join(this.extensionPack.path, filename), yaml);
@@ -255,16 +281,21 @@ export class DataExtensionsEditorView extends AbstractWebview<
255281
const cancellationTokenSource = new CancellationTokenSource();
256282

257283
try {
258-
const queryResult = await runQuery({
259-
cliServer: this.cliServer,
260-
queryRunner: this.queryRunner,
261-
databaseItem: this.databaseItem,
262-
queryStorageDir: this.queryStorageDir,
263-
progress: (progressUpdate: ProgressUpdate) => {
264-
void this.showProgress(progressUpdate, 1500);
284+
const queryResult = await runQuery(
285+
this.mode === Mode.Framework
286+
? "frameworkModeQuery"
287+
: "applicationModeQuery",
288+
{
289+
cliServer: this.cliServer,
290+
queryRunner: this.queryRunner,
291+
databaseItem: this.databaseItem,
292+
queryStorageDir: this.queryStorageDir,
293+
progress: (progressUpdate: ProgressUpdate) => {
294+
void this.showProgress(progressUpdate, 1500);
295+
},
296+
token: cancellationTokenSource.token,
265297
},
266-
token: cancellationTokenSource.token,
267-
});
298+
);
268299
if (!queryResult) {
269300
await this.clearProgress();
270301
return;
@@ -313,29 +344,35 @@ export class DataExtensionsEditorView extends AbstractWebview<
313344
protected async generateModeledMethods(): Promise<void> {
314345
const tokenSource = new CancellationTokenSource();
315346

316-
const selectedDatabase = this.databaseManager.currentDatabaseItem;
317-
318-
// The external API methods are in the library source code, so we need to ask
319-
// the user to import the library database. We need to have the database
320-
// imported to the query server, so we need to register it to our workspace.
321-
const database = await promptImportGithubDatabase(
322-
this.app.commands,
323-
this.databaseManager,
324-
this.app.workspaceStoragePath ?? this.app.globalStoragePath,
325-
this.app.credentials,
326-
(update) => this.showProgress(update),
327-
this.cliServer,
328-
);
329-
if (!database) {
330-
await this.clearProgress();
331-
void this.app.logger.log("No database chosen");
347+
let addedDatabase: DatabaseItem | undefined;
348+
349+
// In application mode, we need the database of a specific library to generate
350+
// the modeled methods. In framework mode, we'll use the current database.
351+
if (this.mode === Mode.Application) {
352+
const selectedDatabase = this.databaseManager.currentDatabaseItem;
353+
354+
// The external API methods are in the library source code, so we need to ask
355+
// the user to import the library database. We need to have the database
356+
// imported to the query server, so we need to register it to our workspace.
357+
addedDatabase = await promptImportGithubDatabase(
358+
this.app.commands,
359+
this.databaseManager,
360+
this.app.workspaceStoragePath ?? this.app.globalStoragePath,
361+
this.app.credentials,
362+
(update) => this.showProgress(update),
363+
this.cliServer,
364+
);
365+
if (!addedDatabase) {
366+
await this.clearProgress();
367+
void this.app.logger.log("No database chosen");
332368

333-
return;
334-
}
369+
return;
370+
}
335371

336-
// The library database was set as the current database by importing it,
337-
// but we need to set it back to the originally selected database.
338-
await this.databaseManager.setCurrentDatabaseItem(selectedDatabase);
372+
// The library database was set as the current database by importing it,
373+
// but we need to set it back to the originally selected database.
374+
await this.databaseManager.setCurrentDatabaseItem(selectedDatabase);
375+
}
339376

340377
await this.showProgress({
341378
step: 0,
@@ -348,7 +385,7 @@ export class DataExtensionsEditorView extends AbstractWebview<
348385
cliServer: this.cliServer,
349386
queryRunner: this.queryRunner,
350387
queryStorageDir: this.queryStorageDir,
351-
databaseItem: database,
388+
databaseItem: addedDatabase ?? this.databaseItem,
352389
onResults: async (results) => {
353390
const modeledMethodsByName: Record<string, ModeledMethod> = {};
354391

@@ -375,14 +412,16 @@ export class DataExtensionsEditorView extends AbstractWebview<
375412
);
376413
}
377414

378-
// After the flow model has been generated, we can remove the temporary database
379-
// which we used for generating the flow model.
380-
await this.showProgress({
381-
step: 3900,
382-
maxStep: 4000,
383-
message: "Removing temporary database",
384-
});
385-
await this.databaseManager.removeDatabaseItem(database);
415+
if (addedDatabase) {
416+
// After the flow model has been generated, we can remove the temporary database
417+
// which we used for generating the flow model.
418+
await this.showProgress({
419+
step: 3900,
420+
maxStep: 4000,
421+
message: "Removing temporary database",
422+
});
423+
await this.databaseManager.removeDatabaseItem(addedDatabase);
424+
}
386425

387426
await this.clearProgress();
388427
}

extensions/ql-vscode/src/data-extensions-editor/extension-pack-name.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export function autoNameExtensionPack(
3737
};
3838
}
3939

40-
function sanitizeExtensionPackName(name: string) {
40+
export function sanitizeExtensionPackName(name: string) {
4141
// Lowercase everything
4242
name = name.toLowerCase();
4343

extensions/ql-vscode/src/data-extensions-editor/external-api-usage-query.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { QueryResultType } from "../query-server/new-messages";
1515
import { join } from "path";
1616
import { redactableError } from "../common/errors";
1717
import { telemetryListener } from "../common/vscode/telemetry";
18+
import { Query } from "./queries/query";
1819

1920
export type RunQueryOptions = {
2021
cliServer: Pick<CodeQLCliServer, "resolveQlpacks">;
@@ -26,14 +27,17 @@ export type RunQueryOptions = {
2627
token: CancellationToken;
2728
};
2829

29-
export async function runQuery({
30-
cliServer,
31-
queryRunner,
32-
databaseItem,
33-
queryStorageDir,
34-
progress,
35-
token,
36-
}: RunQueryOptions): Promise<CoreCompletedQuery | undefined> {
30+
export async function runQuery(
31+
queryName: keyof Omit<Query, "dependencies">,
32+
{
33+
cliServer,
34+
queryRunner,
35+
databaseItem,
36+
queryStorageDir,
37+
progress,
38+
token,
39+
}: RunQueryOptions,
40+
): Promise<CoreCompletedQuery | undefined> {
3741
// The below code is temporary to allow for rapid prototyping of the queries. Once the queries are stabilized, we will
3842
// move these queries into the `github/codeql` repository and use them like any other contextual (e.g. AST) queries.
3943
// This is intentionally not pretty code, as it will be removed soon.
@@ -61,7 +65,7 @@ export async function runQuery({
6165

6266
const queryDir = (await dir({ unsafeCleanup: true })).path;
6367
const queryFile = join(queryDir, "FetchExternalApis.ql");
64-
await writeFile(queryFile, query.mainQuery, "utf8");
68+
await writeFile(queryFile, query[queryName], "utf8");
6569

6670
if (query.dependencies) {
6771
for (const [filename, contents] of Object.entries(query.dependencies)) {

0 commit comments

Comments
 (0)