Generate a QL pack when you add a new database, if one is missing
This commit is contained in:
@@ -26,6 +26,7 @@ import { QueryRunner } from "./queryRunner";
|
|||||||
import { pathsEqual } from "./pure/files";
|
import { pathsEqual } from "./pure/files";
|
||||||
import { redactableError } from "./pure/errors";
|
import { redactableError } from "./pure/errors";
|
||||||
import { isCodespacesTemplate } from "./config";
|
import { isCodespacesTemplate } from "./config";
|
||||||
|
import { QlPackGenerator, QueryLanguage } from "./qlpack-generator";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* databases.ts
|
* databases.ts
|
||||||
@@ -655,9 +656,26 @@ export class DatabaseManager extends DisposableObject {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await showBinaryChoiceDialog(
|
const answer = await showBinaryChoiceDialog(
|
||||||
`We've noticed you don't have a QL pack downloaded to analyze this database. Can we set up a ${databaseItem.language} query pack for you`,
|
`We've noticed you don't have a QL pack downloaded to analyze this database. Can we set up a query pack for you?`,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!answer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const qlPackGenerator = new QlPackGenerator(
|
||||||
|
folderName,
|
||||||
|
databaseItem.language as QueryLanguage,
|
||||||
|
this.cli,
|
||||||
|
);
|
||||||
|
await qlPackGenerator.generate();
|
||||||
|
} catch (e: unknown) {
|
||||||
|
void this.logger.log(
|
||||||
|
`Could not create skeleton QL pack: ${getErrorMessage(e)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async reregisterDatabases(
|
private async reregisterDatabases(
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { testDisposeHandler } from "../test-dispose-handler";
|
|||||||
import { QueryRunner } from "../../../src/queryRunner";
|
import { QueryRunner } from "../../../src/queryRunner";
|
||||||
import * as helpers from "../../../src/helpers";
|
import * as helpers from "../../../src/helpers";
|
||||||
import { Setting } from "../../../src/config";
|
import { Setting } from "../../../src/config";
|
||||||
|
import { QlPackGenerator } from "../../../src/qlpack-generator";
|
||||||
|
|
||||||
describe("databases", () => {
|
describe("databases", () => {
|
||||||
const MOCK_DB_OPTIONS: FullDatabaseOptions = {
|
const MOCK_DB_OPTIONS: FullDatabaseOptions = {
|
||||||
@@ -37,6 +38,7 @@ describe("databases", () => {
|
|||||||
let registerSpy: jest.Mock<Promise<void>, []>;
|
let registerSpy: jest.Mock<Promise<void>, []>;
|
||||||
let deregisterSpy: jest.Mock<Promise<void>, []>;
|
let deregisterSpy: jest.Mock<Promise<void>, []>;
|
||||||
let resolveDatabaseSpy: jest.Mock<Promise<DbInfo>, []>;
|
let resolveDatabaseSpy: jest.Mock<Promise<DbInfo>, []>;
|
||||||
|
let packAddSpy: jest.Mock<any, []>;
|
||||||
let logSpy: jest.Mock<any, []>;
|
let logSpy: jest.Mock<any, []>;
|
||||||
|
|
||||||
let showBinaryChoiceDialogSpy: jest.SpiedFunction<
|
let showBinaryChoiceDialogSpy: jest.SpiedFunction<
|
||||||
@@ -52,6 +54,7 @@ describe("databases", () => {
|
|||||||
registerSpy = jest.fn(() => Promise.resolve(undefined));
|
registerSpy = jest.fn(() => Promise.resolve(undefined));
|
||||||
deregisterSpy = jest.fn(() => Promise.resolve(undefined));
|
deregisterSpy = jest.fn(() => Promise.resolve(undefined));
|
||||||
resolveDatabaseSpy = jest.fn(() => Promise.resolve({} as DbInfo));
|
resolveDatabaseSpy = jest.fn(() => Promise.resolve({} as DbInfo));
|
||||||
|
packAddSpy = jest.fn();
|
||||||
logSpy = jest.fn(() => {
|
logSpy = jest.fn(() => {
|
||||||
/* */
|
/* */
|
||||||
});
|
});
|
||||||
@@ -79,6 +82,7 @@ describe("databases", () => {
|
|||||||
} as unknown as QueryRunner,
|
} as unknown as QueryRunner,
|
||||||
{
|
{
|
||||||
resolveDatabase: resolveDatabaseSpy,
|
resolveDatabase: resolveDatabaseSpy,
|
||||||
|
packAdd: packAddSpy,
|
||||||
} as unknown as CodeQLCliServer,
|
} as unknown as CodeQLCliServer,
|
||||||
{
|
{
|
||||||
log: logSpy,
|
log: logSpy,
|
||||||
@@ -589,20 +593,66 @@ describe("databases", () => {
|
|||||||
|
|
||||||
describe("createSkeletonPacks", () => {
|
describe("createSkeletonPacks", () => {
|
||||||
let mockDbItem: DatabaseItemImpl;
|
let mockDbItem: DatabaseItemImpl;
|
||||||
|
let packfolderName: string;
|
||||||
|
let qlPackYamlFilePath: string;
|
||||||
|
let exampleQlFilePath: string;
|
||||||
|
let language: string;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
language = "ruby";
|
||||||
|
|
||||||
|
const options: FullDatabaseOptions = {
|
||||||
|
dateAdded: 123,
|
||||||
|
ignoreSourceArchive: false,
|
||||||
|
language,
|
||||||
|
};
|
||||||
|
mockDbItem = createMockDB(options);
|
||||||
|
|
||||||
|
packfolderName = `codeql-custom-queries-${mockDbItem.language}`;
|
||||||
|
qlPackYamlFilePath = join(packfolderName, "qlpack.yml");
|
||||||
|
exampleQlFilePath = join(packfolderName, "example.ql");
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
try {
|
||||||
|
fs.rmdirSync(packfolderName, { recursive: true });
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
describe("when the language is set", () => {
|
describe("when the language is set", () => {
|
||||||
it("should offer the user to set up a skeleton QL pack", async () => {
|
it("should offer the user to set up a skeleton QL pack", async () => {
|
||||||
const options: FullDatabaseOptions = {
|
|
||||||
dateAdded: 123,
|
|
||||||
ignoreSourceArchive: false,
|
|
||||||
language: "ruby",
|
|
||||||
};
|
|
||||||
mockDbItem = createMockDB(options);
|
|
||||||
|
|
||||||
await (databaseManager as any).createSkeletonPacks(mockDbItem);
|
await (databaseManager as any).createSkeletonPacks(mockDbItem);
|
||||||
|
|
||||||
expect(showBinaryChoiceDialogSpy).toBeCalledTimes(1);
|
expect(showBinaryChoiceDialogSpy).toBeCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should return early if the user refuses help", async () => {
|
||||||
|
showBinaryChoiceDialogSpy = jest
|
||||||
|
.spyOn(helpers, "showBinaryChoiceDialog")
|
||||||
|
.mockResolvedValue(false);
|
||||||
|
|
||||||
|
const generateSpy = jest.spyOn(QlPackGenerator.prototype, "generate");
|
||||||
|
|
||||||
|
await (databaseManager as any).createSkeletonPacks(mockDbItem);
|
||||||
|
|
||||||
|
expect(generateSpy).not.toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create the skeleton QL pack for the user", async () => {
|
||||||
|
expect(fs.existsSync(packfolderName)).toBe(false);
|
||||||
|
expect(fs.existsSync(qlPackYamlFilePath)).toBe(false);
|
||||||
|
expect(fs.existsSync(exampleQlFilePath)).toBe(false);
|
||||||
|
|
||||||
|
await (databaseManager as any).createSkeletonPacks(mockDbItem);
|
||||||
|
|
||||||
|
expect(fs.existsSync(packfolderName)).toBe(true);
|
||||||
|
expect(fs.existsSync(qlPackYamlFilePath)).toBe(true);
|
||||||
|
expect(fs.existsSync(exampleQlFilePath)).toBe(true);
|
||||||
|
|
||||||
|
expect(packAddSpy).toHaveBeenCalledWith(packfolderName, language);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when the language is not set", () => {
|
describe("when the language is not set", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user