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 {
|
import {
|
||||||
ensureDirSync,
|
ensureDirSync,
|
||||||
readFile,
|
|
||||||
pathExists,
|
pathExists,
|
||||||
ensureDir,
|
ensureDir,
|
||||||
writeFile,
|
writeFile,
|
||||||
opendir,
|
opendir,
|
||||||
} from "fs-extra";
|
} from "fs-extra";
|
||||||
import { glob } from "glob";
|
import { glob } from "glob";
|
||||||
import { load } from "js-yaml";
|
|
||||||
import { join, basename, dirname } from "path";
|
import { join, basename, dirname } from "path";
|
||||||
import { dirSync } from "tmp-promise";
|
import { dirSync } from "tmp-promise";
|
||||||
import { Uri, window as Window, workspace, env, WorkspaceFolder } from "vscode";
|
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 { UserCancellationException } from "./common/vscode/progress";
|
||||||
import { extLogger, OutputChannelLogger } from "./common";
|
import { extLogger, OutputChannelLogger } from "./common";
|
||||||
import { QueryMetadata } from "./pure/interface-types";
|
import { QueryMetadata } from "./pure/interface-types";
|
||||||
import { telemetryListener } from "./telemetry";
|
import { telemetryListener } from "./telemetry";
|
||||||
import { RedactableError } from "./pure/errors";
|
import { RedactableError } from "./pure/errors";
|
||||||
import { getQlPackPath } from "./pure/ql";
|
|
||||||
import { dbSchemeToLanguage, QueryLanguage } from "./common/query-language";
|
import { dbSchemeToLanguage, QueryLanguage } from "./common/query-language";
|
||||||
import { isCodespacesTemplate } from "./config";
|
import { isCodespacesTemplate } from "./config";
|
||||||
import { AppCommandManager } from "./common/commands";
|
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.
|
* 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 { basename, dirname, resolve } from "path";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getPrimaryDbscheme,
|
|
||||||
getQlPackForDbscheme,
|
|
||||||
getOnDiskWorkspaceFolders,
|
getOnDiskWorkspaceFolders,
|
||||||
QlPacksForLanguage,
|
|
||||||
showAndLogExceptionWithTelemetry,
|
showAndLogExceptionWithTelemetry,
|
||||||
} from "../../helpers";
|
} from "../../helpers";
|
||||||
|
import {
|
||||||
|
getPrimaryDbscheme,
|
||||||
|
getQlPackForDbscheme,
|
||||||
|
QlPacksForLanguage,
|
||||||
|
} from "../../databases/qlpack";
|
||||||
import {
|
import {
|
||||||
KeyType,
|
KeyType,
|
||||||
kindOfKeyType,
|
kindOfKeyType,
|
||||||
|
|||||||
@@ -5,12 +5,8 @@ import { CancellationToken, window as Window, workspace, Uri } from "vscode";
|
|||||||
import { LSPErrorCodes, ResponseError } from "vscode-languageclient";
|
import { LSPErrorCodes, ResponseError } from "vscode-languageclient";
|
||||||
import { CodeQLCliServer } from "../codeql-cli/cli";
|
import { CodeQLCliServer } from "../codeql-cli/cli";
|
||||||
import { DatabaseUI } from "../databases/local-databases-ui";
|
import { DatabaseUI } from "../databases/local-databases-ui";
|
||||||
import {
|
import { getInitialQueryContents, showBinaryChoiceDialog } from "../helpers";
|
||||||
getInitialQueryContents,
|
import { getPrimaryDbscheme, getQlPackForDbscheme } from "../databases/qlpack";
|
||||||
getPrimaryDbscheme,
|
|
||||||
getQlPackForDbscheme,
|
|
||||||
showBinaryChoiceDialog,
|
|
||||||
} from "../helpers";
|
|
||||||
import {
|
import {
|
||||||
ProgressCallback,
|
ProgressCallback,
|
||||||
UserCancellationException,
|
UserCancellationException,
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ import {
|
|||||||
import { itWithCodeQL } from "../cli";
|
import { itWithCodeQL } from "../cli";
|
||||||
import {
|
import {
|
||||||
getOnDiskWorkspaceFolders,
|
getOnDiskWorkspaceFolders,
|
||||||
getQlPackForDbscheme,
|
|
||||||
languageToDbScheme,
|
languageToDbScheme,
|
||||||
} from "../../../src/helpers";
|
} from "../../../src/helpers";
|
||||||
import { KeyType, resolveQueries } from "../../../src/language-support";
|
import { KeyType, resolveQueries } from "../../../src/language-support";
|
||||||
import { faker } from "@faker-js/faker";
|
import { faker } from "@faker-js/faker";
|
||||||
import { getActivatedExtension } from "../global.helper";
|
import { getActivatedExtension } from "../global.helper";
|
||||||
import { BaseLogger } from "../../../src/common";
|
import { BaseLogger } from "../../../src/common";
|
||||||
|
import { getQlPackForDbscheme } from "../../../src/databases/qlpack";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform proper integration tests by running the CLI
|
* 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 { getErrorMessage } from "../../../../../src/pure/helpers-pure";
|
||||||
|
|
||||||
import * as helpers from "../../../../../src/helpers";
|
import * as helpers from "../../../../../src/helpers";
|
||||||
|
import * as qlpack from "../../../../../src/databases/qlpack";
|
||||||
import {
|
import {
|
||||||
KeyType,
|
KeyType,
|
||||||
qlpackOfDatabase,
|
qlpackOfDatabase,
|
||||||
@@ -14,10 +15,10 @@ import { mockDatabaseItem, mockedObject } from "../../../utils/mocking.helpers";
|
|||||||
|
|
||||||
describe("queryResolver", () => {
|
describe("queryResolver", () => {
|
||||||
let getQlPackForDbschemeSpy: jest.SpiedFunction<
|
let getQlPackForDbschemeSpy: jest.SpiedFunction<
|
||||||
typeof helpers.getQlPackForDbscheme
|
typeof qlpack.getQlPackForDbscheme
|
||||||
>;
|
>;
|
||||||
let getPrimaryDbschemeSpy: jest.SpiedFunction<
|
let getPrimaryDbschemeSpy: jest.SpiedFunction<
|
||||||
typeof helpers.getPrimaryDbscheme
|
typeof qlpack.getPrimaryDbscheme
|
||||||
>;
|
>;
|
||||||
|
|
||||||
const resolveQueriesInSuite = jest.fn();
|
const resolveQueriesInSuite = jest.fn();
|
||||||
@@ -28,13 +29,13 @@ describe("queryResolver", () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
getQlPackForDbschemeSpy = jest
|
getQlPackForDbschemeSpy = jest
|
||||||
.spyOn(helpers, "getQlPackForDbscheme")
|
.spyOn(qlpack, "getQlPackForDbscheme")
|
||||||
.mockResolvedValue({
|
.mockResolvedValue({
|
||||||
dbschemePack: "dbschemePack",
|
dbschemePack: "dbschemePack",
|
||||||
dbschemePackIsLibraryPack: false,
|
dbschemePackIsLibraryPack: false,
|
||||||
});
|
});
|
||||||
getPrimaryDbschemeSpy = jest
|
getPrimaryDbschemeSpy = jest
|
||||||
.spyOn(helpers, "getPrimaryDbscheme")
|
.spyOn(qlpack, "getPrimaryDbscheme")
|
||||||
.mockResolvedValue("primaryDbscheme");
|
.mockResolvedValue("primaryDbscheme");
|
||||||
|
|
||||||
jest.spyOn(helpers, "getOnDiskWorkspaceFolders").mockReturnValue([]);
|
jest.spyOn(helpers, "getOnDiskWorkspaceFolders").mockReturnValue([]);
|
||||||
|
|||||||
Reference in New Issue
Block a user