Skip to content

Commit dce94e8

Browse files
Merge pull request #2341 from github/yer-an-input-box-query
Prompt non-codespace users for storage path
2 parents 3f7b8a6 + 7335e37 commit dce94e8

File tree

4 files changed

+178
-2
lines changed

4 files changed

+178
-2
lines changed

extensions/ql-vscode/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,12 @@
340340
"type": "boolean",
341341
"default": false,
342342
"description": "Allow database to be downloaded via HTTP. Warning: enabling this option will allow downloading from insecure servers."
343+
},
344+
"codeQL.createQuery.folder": {
345+
"type": "string",
346+
"default": "",
347+
"patternErrorMessage": "Please enter a valid folder",
348+
"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."
343349
}
344350
}
345351
},

extensions/ql-vscode/src/config.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,3 +619,19 @@ export const ALLOW_HTTP_SETTING = new Setting(
619619
export function allowHttp(): boolean {
620620
return ALLOW_HTTP_SETTING.getValue<boolean>() || false;
621621
}
622+
623+
/**
624+
* The name of the folder where we want to create skeleton wizard QL packs.
625+
**/
626+
const SKELETON_WIZARD_FOLDER = new Setting(
627+
"folder",
628+
new Setting("createQuery", ROOT_SETTING),
629+
);
630+
631+
export function getSkeletonWizardFolder(): string | undefined {
632+
return SKELETON_WIZARD_FOLDER.getValue<string>() || undefined;
633+
}
634+
635+
export async function setSkeletonWizardFolder(folder: string | undefined) {
636+
await SKELETON_WIZARD_FOLDER.updateValue(folder, ConfigurationTarget.Global);
637+
}

extensions/ql-vscode/src/skeleton-query-wizard.ts

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ import { QlPackGenerator } from "./qlpack-generator";
1414
import { DatabaseItem, DatabaseManager } from "./local-databases";
1515
import { ProgressCallback, UserCancellationException } from "./progress";
1616
import { askForGitHubRepo, downloadGitHubDatabase } from "./databaseFetcher";
17-
import { existsSync } from "fs";
17+
import {
18+
getSkeletonWizardFolder,
19+
isCodespacesTemplate,
20+
setSkeletonWizardFolder,
21+
} from "./config";
22+
import { existsSync } from "fs-extra";
1823

1924
type QueryLanguagesToDatabaseMap = Record<string, string>;
2025

@@ -55,7 +60,7 @@ export class SkeletonQueryWizard {
5560
return;
5661
}
5762

58-
this.qlPackStoragePath = getFirstWorkspaceFolder();
63+
this.qlPackStoragePath = await this.determineStoragePath();
5964

6065
const skeletonPackAlreadyExists =
6166
existsSync(join(this.qlPackStoragePath, this.folderName)) ||
@@ -97,6 +102,38 @@ export class SkeletonQueryWizard {
97102
});
98103
}
99104

105+
public async determineStoragePath() {
106+
const firstStorageFolder = getFirstWorkspaceFolder();
107+
108+
if (isCodespacesTemplate()) {
109+
return firstStorageFolder;
110+
}
111+
112+
let storageFolder = getSkeletonWizardFolder();
113+
114+
if (storageFolder === undefined || !existsSync(storageFolder)) {
115+
storageFolder = await Window.showInputBox({
116+
title:
117+
"Please choose a folder in which to create your new query pack. You can change this in the extension settings.",
118+
value: firstStorageFolder,
119+
ignoreFocusOut: true,
120+
});
121+
}
122+
123+
if (storageFolder === undefined) {
124+
throw new UserCancellationException("No storage folder entered.");
125+
}
126+
127+
if (!existsSync(storageFolder)) {
128+
throw new UserCancellationException(
129+
"Invalid folder. Must be a folder that already exists.",
130+
);
131+
}
132+
133+
await setSkeletonWizardFolder(storageFolder);
134+
return storageFolder;
135+
}
136+
100137
private async chooseLanguage() {
101138
this.progress({
102139
message: "Choose language",

extensions/ql-vscode/test/vscode-tests/cli-integration/skeleton-query-wizard.test.ts

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
import * as databaseFetcher from "../../../src/databaseFetcher";
2222
import { createMockDB } from "../../factories/databases/databases";
2323
import { asError } from "../../../src/pure/helpers-pure";
24+
import { Setting } from "../../../src/config";
2425

2526
describe("SkeletonQueryWizard", () => {
2627
let mockCli: CodeQLCliServer;
@@ -29,6 +30,7 @@ describe("SkeletonQueryWizard", () => {
2930
let dir: tmp.DirResult;
3031
let storagePath: string;
3132
let quickPickSpy: jest.SpiedFunction<typeof window.showQuickPick>;
33+
let showInputBoxSpy: jest.SpiedFunction<typeof window.showInputBox>;
3234
let generateSpy: jest.SpiedFunction<
3335
typeof QlPackGenerator.prototype.generate
3436
>;
@@ -93,6 +95,9 @@ describe("SkeletonQueryWizard", () => {
9395
quickPickSpy = jest
9496
.spyOn(window, "showQuickPick")
9597
.mockResolvedValueOnce(mockedQuickPickItem(chosenLanguage));
98+
showInputBoxSpy = jest
99+
.spyOn(window, "showInputBox")
100+
.mockResolvedValue(storagePath);
96101
generateSpy = jest
97102
.spyOn(QlPackGenerator.prototype, "generate")
98103
.mockResolvedValue(undefined);
@@ -433,4 +438,116 @@ describe("SkeletonQueryWizard", () => {
433438
});
434439
});
435440
});
441+
442+
describe("determineStoragePath", () => {
443+
it("should prompt the user to provide a storage path", async () => {
444+
const chosenPath = await wizard.determineStoragePath();
445+
446+
expect(showInputBoxSpy).toHaveBeenCalledWith(
447+
expect.objectContaining({ value: storagePath }),
448+
);
449+
expect(chosenPath).toEqual(storagePath);
450+
});
451+
452+
it("should write the chosen folder to settings", async () => {
453+
const updateValueSpy = jest.spyOn(Setting.prototype, "updateValue");
454+
455+
await wizard.determineStoragePath();
456+
457+
expect(updateValueSpy).toHaveBeenCalledWith(storagePath, 1);
458+
});
459+
460+
describe("when the user is using the codespace template", () => {
461+
let originalValue: any;
462+
let storedPath: string;
463+
464+
beforeEach(async () => {
465+
storedPath = join(dir.name, "pickles-folder");
466+
ensureDirSync(storedPath);
467+
468+
originalValue = workspace
469+
.getConfiguration("codeQL.createQuery")
470+
.get("folder");
471+
472+
// Set isCodespacesTemplate to true to indicate we are in the codespace template
473+
await workspace
474+
.getConfiguration("codeQL")
475+
.update("codespacesTemplate", true);
476+
});
477+
478+
afterEach(async () => {
479+
await workspace
480+
.getConfiguration("codeQL")
481+
.update("codespacesTemplate", originalValue);
482+
});
483+
484+
it("should not prompt the user", async () => {
485+
const chosenPath = await wizard.determineStoragePath();
486+
487+
expect(showInputBoxSpy).not.toHaveBeenCalled();
488+
expect(chosenPath).toEqual(storagePath);
489+
});
490+
});
491+
492+
describe("when there is already a saved storage path in settings", () => {
493+
describe("when the saved storage path exists", () => {
494+
let originalValue: any;
495+
let storedPath: string;
496+
497+
beforeEach(async () => {
498+
storedPath = join(dir.name, "pickles-folder");
499+
ensureDirSync(storedPath);
500+
501+
originalValue = workspace
502+
.getConfiguration("codeQL.createQuery")
503+
.get("folder");
504+
await workspace
505+
.getConfiguration("codeQL.createQuery")
506+
.update("folder", storedPath);
507+
});
508+
509+
afterEach(async () => {
510+
await workspace
511+
.getConfiguration("codeQL.createQuery")
512+
.update("folder", originalValue);
513+
});
514+
515+
it("should return it and not prompt the user", async () => {
516+
const chosenPath = await wizard.determineStoragePath();
517+
518+
expect(showInputBoxSpy).not.toHaveBeenCalled();
519+
expect(chosenPath).toEqual(storedPath);
520+
});
521+
});
522+
523+
describe("when the saved storage path does not exist", () => {
524+
let originalValue: any;
525+
let storedPath: string;
526+
527+
beforeEach(async () => {
528+
storedPath = join(dir.name, "this-folder-does-not-exist");
529+
530+
originalValue = workspace
531+
.getConfiguration("codeQL.createQuery")
532+
.get("folder");
533+
await workspace
534+
.getConfiguration("codeQL.createQuery")
535+
.update("folder", storedPath);
536+
});
537+
538+
afterEach(async () => {
539+
await workspace
540+
.getConfiguration("codeQL.createQuery")
541+
.update("folder", originalValue);
542+
});
543+
544+
it("should prompt the user for to provide a new folder name", async () => {
545+
const chosenPath = await wizard.determineStoragePath();
546+
547+
expect(showInputBoxSpy).toHaveBeenCalled();
548+
expect(chosenPath).toEqual(storagePath);
549+
});
550+
});
551+
});
552+
});
436553
});

0 commit comments

Comments
 (0)