Move qlpacks helpers to separate file
This commit is contained in:
130
extensions/ql-vscode/src/databases/qlpack.ts
Normal file
130
extensions/ql-vscode/src/databases/qlpack.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import { window } from "vscode";
|
||||
import { glob } from "glob";
|
||||
import { basename } from "path";
|
||||
import { load } from "js-yaml";
|
||||
import { readFile } from "fs-extra";
|
||||
import { getQlPackPath } from "../pure/ql";
|
||||
import { CodeQLCliServer, QlpacksInfo } from "../codeql-cli/cli";
|
||||
import { extLogger } from "../common";
|
||||
import { getOnDiskWorkspaceFolders } from "../helpers";
|
||||
|
||||
export interface QlPacksForLanguage {
|
||||
/** The name of the pack containing the dbscheme. */
|
||||
dbschemePack: string;
|
||||
/** `true` if `dbschemePack` is a library pack. */
|
||||
dbschemePackIsLibraryPack: boolean;
|
||||
/**
|
||||
* The name of the corresponding standard query pack.
|
||||
* Only defined if `dbschemePack` is a library pack.
|
||||
*/
|
||||
queryPack?: string;
|
||||
}
|
||||
|
||||
interface QlPackWithPath {
|
||||
packName: string;
|
||||
packDir: string | undefined;
|
||||
}
|
||||
|
||||
async function findDbschemePack(
|
||||
packs: QlPackWithPath[],
|
||||
dbschemePath: string,
|
||||
): Promise<{ name: string; isLibraryPack: boolean }> {
|
||||
for (const { packDir, packName } of packs) {
|
||||
if (packDir !== undefined) {
|
||||
const qlpackPath = await getQlPackPath(packDir);
|
||||
|
||||
if (qlpackPath !== undefined) {
|
||||
const qlpack = load(await readFile(qlpackPath, "utf8")) as {
|
||||
dbscheme?: string;
|
||||
library?: boolean;
|
||||
};
|
||||
if (
|
||||
qlpack.dbscheme !== undefined &&
|
||||
basename(qlpack.dbscheme) === basename(dbschemePath)
|
||||
) {
|
||||
return {
|
||||
name: packName,
|
||||
isLibraryPack: qlpack.library === true,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error(`Could not find qlpack file for dbscheme ${dbschemePath}`);
|
||||
}
|
||||
|
||||
function findStandardQueryPack(
|
||||
qlpacks: QlpacksInfo,
|
||||
dbschemePackName: string,
|
||||
): string | undefined {
|
||||
const matches = dbschemePackName.match(/^codeql\/(?<language>[a-z]+)-all$/);
|
||||
if (matches) {
|
||||
const queryPackName = `codeql/${matches.groups!.language}-queries`;
|
||||
if (qlpacks[queryPackName] !== undefined) {
|
||||
return queryPackName;
|
||||
}
|
||||
}
|
||||
|
||||
// Either the dbscheme pack didn't look like one where the queries might be in the query pack, or
|
||||
// no query pack was found in the search path. Either is OK.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export async function getQlPackForDbscheme(
|
||||
cliServer: Pick<CodeQLCliServer, "resolveQlpacks">,
|
||||
dbschemePath: string,
|
||||
): Promise<QlPacksForLanguage> {
|
||||
const qlpacks = await cliServer.resolveQlpacks(getOnDiskWorkspaceFolders());
|
||||
const packs: QlPackWithPath[] = Object.entries(qlpacks).map(
|
||||
([packName, dirs]) => {
|
||||
if (dirs.length < 1) {
|
||||
void extLogger.log(
|
||||
`In getQlPackFor ${dbschemePath}, qlpack ${packName} has no directories`,
|
||||
);
|
||||
return { packName, packDir: undefined };
|
||||
}
|
||||
if (dirs.length > 1) {
|
||||
void extLogger.log(
|
||||
`In getQlPackFor ${dbschemePath}, qlpack ${packName} has more than one directory; arbitrarily choosing the first`,
|
||||
);
|
||||
}
|
||||
return {
|
||||
packName,
|
||||
packDir: dirs[0],
|
||||
};
|
||||
},
|
||||
);
|
||||
const dbschemePack = await findDbschemePack(packs, dbschemePath);
|
||||
const queryPack = dbschemePack.isLibraryPack
|
||||
? findStandardQueryPack(qlpacks, dbschemePack.name)
|
||||
: undefined;
|
||||
return {
|
||||
dbschemePack: dbschemePack.name,
|
||||
dbschemePackIsLibraryPack: dbschemePack.isLibraryPack,
|
||||
queryPack,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getPrimaryDbscheme(
|
||||
datasetFolder: string,
|
||||
): Promise<string> {
|
||||
const dbschemes = await glob("*.dbscheme", {
|
||||
cwd: datasetFolder,
|
||||
});
|
||||
|
||||
if (dbschemes.length < 1) {
|
||||
throw new Error(
|
||||
`Can't find dbscheme for current database in ${datasetFolder}`,
|
||||
);
|
||||
}
|
||||
|
||||
dbschemes.sort();
|
||||
const dbscheme = dbschemes[0];
|
||||
|
||||
if (dbschemes.length > 1) {
|
||||
void window.showErrorMessage(
|
||||
`Found multiple dbschemes in ${datasetFolder} during quick query; arbitrarily choosing the first, ${dbscheme}, to decide what library to use.`,
|
||||
);
|
||||
}
|
||||
return dbscheme;
|
||||
}
|
||||
@@ -1,23 +1,20 @@
|
||||
import {
|
||||
ensureDirSync,
|
||||
readFile,
|
||||
pathExists,
|
||||
ensureDir,
|
||||
writeFile,
|
||||
opendir,
|
||||
} from "fs-extra";
|
||||
import { glob } from "glob";
|
||||
import { load } from "js-yaml";
|
||||
import { join, basename, dirname } from "path";
|
||||
import { dirSync } from "tmp-promise";
|
||||
import { Uri, window as Window, workspace, env, WorkspaceFolder } from "vscode";
|
||||
import { CodeQLCliServer, QlpacksInfo } from "./codeql-cli/cli";
|
||||
import { CodeQLCliServer } from "./codeql-cli/cli";
|
||||
import { UserCancellationException } from "./common/vscode/progress";
|
||||
import { extLogger, OutputChannelLogger } from "./common";
|
||||
import { QueryMetadata } from "./pure/interface-types";
|
||||
import { telemetryListener } from "./telemetry";
|
||||
import { RedactableError } from "./pure/errors";
|
||||
import { getQlPackPath } from "./pure/ql";
|
||||
import { dbSchemeToLanguage, QueryLanguage } from "./common/query-language";
|
||||
import { isCodespacesTemplate } from "./config";
|
||||
import { AppCommandManager } from "./common/commands";
|
||||
@@ -356,127 +353,6 @@ export async function prepareCodeTour(
|
||||
}
|
||||
}
|
||||
|
||||
export interface QlPacksForLanguage {
|
||||
/** The name of the pack containing the dbscheme. */
|
||||
dbschemePack: string;
|
||||
/** `true` if `dbschemePack` is a library pack. */
|
||||
dbschemePackIsLibraryPack: boolean;
|
||||
/**
|
||||
* The name of the corresponding standard query pack.
|
||||
* Only defined if `dbschemePack` is a library pack.
|
||||
*/
|
||||
queryPack?: string;
|
||||
}
|
||||
|
||||
interface QlPackWithPath {
|
||||
packName: string;
|
||||
packDir: string | undefined;
|
||||
}
|
||||
|
||||
async function findDbschemePack(
|
||||
packs: QlPackWithPath[],
|
||||
dbschemePath: string,
|
||||
): Promise<{ name: string; isLibraryPack: boolean }> {
|
||||
for (const { packDir, packName } of packs) {
|
||||
if (packDir !== undefined) {
|
||||
const qlpackPath = await getQlPackPath(packDir);
|
||||
|
||||
if (qlpackPath !== undefined) {
|
||||
const qlpack = load(await readFile(qlpackPath, "utf8")) as {
|
||||
dbscheme?: string;
|
||||
library?: boolean;
|
||||
};
|
||||
if (
|
||||
qlpack.dbscheme !== undefined &&
|
||||
basename(qlpack.dbscheme) === basename(dbschemePath)
|
||||
) {
|
||||
return {
|
||||
name: packName,
|
||||
isLibraryPack: qlpack.library === true,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error(`Could not find qlpack file for dbscheme ${dbschemePath}`);
|
||||
}
|
||||
|
||||
function findStandardQueryPack(
|
||||
qlpacks: QlpacksInfo,
|
||||
dbschemePackName: string,
|
||||
): string | undefined {
|
||||
const matches = dbschemePackName.match(/^codeql\/(?<language>[a-z]+)-all$/);
|
||||
if (matches) {
|
||||
const queryPackName = `codeql/${matches.groups!.language}-queries`;
|
||||
if (qlpacks[queryPackName] !== undefined) {
|
||||
return queryPackName;
|
||||
}
|
||||
}
|
||||
|
||||
// Either the dbscheme pack didn't look like one where the queries might be in the query pack, or
|
||||
// no query pack was found in the search path. Either is OK.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export async function getQlPackForDbscheme(
|
||||
cliServer: Pick<CodeQLCliServer, "resolveQlpacks">,
|
||||
dbschemePath: string,
|
||||
): Promise<QlPacksForLanguage> {
|
||||
const qlpacks = await cliServer.resolveQlpacks(getOnDiskWorkspaceFolders());
|
||||
const packs: QlPackWithPath[] = Object.entries(qlpacks).map(
|
||||
([packName, dirs]) => {
|
||||
if (dirs.length < 1) {
|
||||
void extLogger.log(
|
||||
`In getQlPackFor ${dbschemePath}, qlpack ${packName} has no directories`,
|
||||
);
|
||||
return { packName, packDir: undefined };
|
||||
}
|
||||
if (dirs.length > 1) {
|
||||
void extLogger.log(
|
||||
`In getQlPackFor ${dbschemePath}, qlpack ${packName} has more than one directory; arbitrarily choosing the first`,
|
||||
);
|
||||
}
|
||||
return {
|
||||
packName,
|
||||
packDir: dirs[0],
|
||||
};
|
||||
},
|
||||
);
|
||||
const dbschemePack = await findDbschemePack(packs, dbschemePath);
|
||||
const queryPack = dbschemePack.isLibraryPack
|
||||
? findStandardQueryPack(qlpacks, dbschemePack.name)
|
||||
: undefined;
|
||||
return {
|
||||
dbschemePack: dbschemePack.name,
|
||||
dbschemePackIsLibraryPack: dbschemePack.isLibraryPack,
|
||||
queryPack,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getPrimaryDbscheme(
|
||||
datasetFolder: string,
|
||||
): Promise<string> {
|
||||
const dbschemes = await glob("*.dbscheme", {
|
||||
cwd: datasetFolder,
|
||||
});
|
||||
|
||||
if (dbschemes.length < 1) {
|
||||
throw new Error(
|
||||
`Can't find dbscheme for current database in ${datasetFolder}`,
|
||||
);
|
||||
}
|
||||
|
||||
dbschemes.sort();
|
||||
const dbscheme = dbschemes[0];
|
||||
|
||||
if (dbschemes.length > 1) {
|
||||
void Window.showErrorMessage(
|
||||
`Found multiple dbschemes in ${datasetFolder} during quick query; arbitrarily choosing the first, ${dbscheme}, to decide what library to use.`,
|
||||
);
|
||||
}
|
||||
return dbscheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* The following functions al heuristically determine metadata about databases.
|
||||
*/
|
||||
|
||||
@@ -4,12 +4,14 @@ import { file } from "tmp-promise";
|
||||
import { basename, dirname, resolve } from "path";
|
||||
|
||||
import {
|
||||
getPrimaryDbscheme,
|
||||
getQlPackForDbscheme,
|
||||
getOnDiskWorkspaceFolders,
|
||||
QlPacksForLanguage,
|
||||
showAndLogExceptionWithTelemetry,
|
||||
} from "../../helpers";
|
||||
import {
|
||||
getPrimaryDbscheme,
|
||||
getQlPackForDbscheme,
|
||||
QlPacksForLanguage,
|
||||
} from "../../databases/qlpack";
|
||||
import {
|
||||
KeyType,
|
||||
kindOfKeyType,
|
||||
|
||||
@@ -5,12 +5,8 @@ import { CancellationToken, window as Window, workspace, Uri } from "vscode";
|
||||
import { LSPErrorCodes, ResponseError } from "vscode-languageclient";
|
||||
import { CodeQLCliServer } from "../codeql-cli/cli";
|
||||
import { DatabaseUI } from "../databases/local-databases-ui";
|
||||
import {
|
||||
getInitialQueryContents,
|
||||
getPrimaryDbscheme,
|
||||
getQlPackForDbscheme,
|
||||
showBinaryChoiceDialog,
|
||||
} from "../helpers";
|
||||
import { getInitialQueryContents, showBinaryChoiceDialog } from "../helpers";
|
||||
import { getPrimaryDbscheme, getQlPackForDbscheme } from "../databases/qlpack";
|
||||
import {
|
||||
ProgressCallback,
|
||||
UserCancellationException,
|
||||
|
||||
@@ -9,13 +9,13 @@ import {
|
||||
import { itWithCodeQL } from "../cli";
|
||||
import {
|
||||
getOnDiskWorkspaceFolders,
|
||||
getQlPackForDbscheme,
|
||||
languageToDbScheme,
|
||||
} from "../../../src/helpers";
|
||||
import { KeyType, resolveQueries } from "../../../src/language-support";
|
||||
import { faker } from "@faker-js/faker";
|
||||
import { getActivatedExtension } from "../global.helper";
|
||||
import { BaseLogger } from "../../../src/common";
|
||||
import { getQlPackForDbscheme } from "../../../src/databases/qlpack";
|
||||
|
||||
/**
|
||||
* Perform proper integration tests by running the CLI
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as fs from "fs-extra";
|
||||
import { getErrorMessage } from "../../../../../src/pure/helpers-pure";
|
||||
|
||||
import * as helpers from "../../../../../src/helpers";
|
||||
import * as qlpack from "../../../../../src/databases/qlpack";
|
||||
import {
|
||||
KeyType,
|
||||
qlpackOfDatabase,
|
||||
@@ -14,10 +15,10 @@ import { mockDatabaseItem, mockedObject } from "../../../utils/mocking.helpers";
|
||||
|
||||
describe("queryResolver", () => {
|
||||
let getQlPackForDbschemeSpy: jest.SpiedFunction<
|
||||
typeof helpers.getQlPackForDbscheme
|
||||
typeof qlpack.getQlPackForDbscheme
|
||||
>;
|
||||
let getPrimaryDbschemeSpy: jest.SpiedFunction<
|
||||
typeof helpers.getPrimaryDbscheme
|
||||
typeof qlpack.getPrimaryDbscheme
|
||||
>;
|
||||
|
||||
const resolveQueriesInSuite = jest.fn();
|
||||
@@ -28,13 +29,13 @@ describe("queryResolver", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
getQlPackForDbschemeSpy = jest
|
||||
.spyOn(helpers, "getQlPackForDbscheme")
|
||||
.spyOn(qlpack, "getQlPackForDbscheme")
|
||||
.mockResolvedValue({
|
||||
dbschemePack: "dbschemePack",
|
||||
dbschemePackIsLibraryPack: false,
|
||||
});
|
||||
getPrimaryDbschemeSpy = jest
|
||||
.spyOn(helpers, "getPrimaryDbscheme")
|
||||
.spyOn(qlpack, "getPrimaryDbscheme")
|
||||
.mockResolvedValue("primaryDbscheme");
|
||||
|
||||
jest.spyOn(helpers, "getOnDiskWorkspaceFolders").mockReturnValue([]);
|
||||
|
||||
Reference in New Issue
Block a user