Use QueryLanguage type for representing query languages
This will make both the `askForLanguage` and `findLanguage` functions return a `QueryLanguage` instead of a `string`. This will make it harder to make mistakes when using these functions. There are also some other changes with regards to `QueryLanguage` such that we never need to use `as QueryLanguage` explicitly anymore, except for the new `isQueryLanguage` function. The only remaining place that I know of where we're using a `string` to represent the `QueryLanguage` is in a database item's language, but this is harder to change and may be relied upon by language authors.
This commit is contained in:
@@ -4,6 +4,7 @@ import { writeFile } from "fs-extra";
|
|||||||
import { dump as dumpYaml } from "js-yaml";
|
import { dump as dumpYaml } from "js-yaml";
|
||||||
import {
|
import {
|
||||||
getOnDiskWorkspaceFolders,
|
getOnDiskWorkspaceFolders,
|
||||||
|
isQueryLanguage,
|
||||||
showAndLogExceptionWithTelemetry,
|
showAndLogExceptionWithTelemetry,
|
||||||
} from "../helpers";
|
} from "../helpers";
|
||||||
import { TeeLogger } from "../common";
|
import { TeeLogger } from "../common";
|
||||||
@@ -15,7 +16,6 @@ import { fetchExternalApiQueries } from "./queries";
|
|||||||
import { QueryResultType } from "../pure/new-messages";
|
import { QueryResultType } from "../pure/new-messages";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import { redactableError } from "../pure/errors";
|
import { redactableError } from "../pure/errors";
|
||||||
import { QueryLanguage } from "../common/query-language";
|
|
||||||
|
|
||||||
export type RunQueryOptions = {
|
export type RunQueryOptions = {
|
||||||
cliServer: Pick<CodeQLCliServer, "resolveQlpacks">;
|
cliServer: Pick<CodeQLCliServer, "resolveQlpacks">;
|
||||||
@@ -41,7 +41,14 @@ export async function runQuery({
|
|||||||
// For a reference of what this should do in the future, see the previous implementation in
|
// For a reference of what this should do in the future, see the previous implementation in
|
||||||
// https://github.com/github/vscode-codeql/blob/089d3566ef0bc67d9b7cc66e8fd6740b31c1c0b0/extensions/ql-vscode/src/data-extensions-editor/external-api-usage-query.ts#L33-L72
|
// https://github.com/github/vscode-codeql/blob/089d3566ef0bc67d9b7cc66e8fd6740b31c1c0b0/extensions/ql-vscode/src/data-extensions-editor/external-api-usage-query.ts#L33-L72
|
||||||
|
|
||||||
const query = fetchExternalApiQueries[databaseItem.language as QueryLanguage];
|
if (!isQueryLanguage(databaseItem.language)) {
|
||||||
|
void showAndLogExceptionWithTelemetry(
|
||||||
|
redactableError`Unsupported database language ${databaseItem.language}`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = fetchExternalApiQueries[databaseItem.language];
|
||||||
if (!query) {
|
if (!query) {
|
||||||
void showAndLogExceptionWithTelemetry(
|
void showAndLogExceptionWithTelemetry(
|
||||||
redactableError`No external API usage query found for language ${databaseItem.language}`,
|
redactableError`No external API usage query found for language ${databaseItem.language}`,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
isFolderAlreadyInWorkspace,
|
isFolderAlreadyInWorkspace,
|
||||||
getFirstWorkspaceFolder,
|
getFirstWorkspaceFolder,
|
||||||
showNeverAskAgainDialog,
|
showNeverAskAgainDialog,
|
||||||
|
isQueryLanguage,
|
||||||
} from "../helpers";
|
} from "../helpers";
|
||||||
import { ProgressCallback, withProgress } from "../common/vscode/progress";
|
import { ProgressCallback, withProgress } from "../common/vscode/progress";
|
||||||
import {
|
import {
|
||||||
@@ -32,7 +33,6 @@ import {
|
|||||||
setAutogenerateQlPacks,
|
setAutogenerateQlPacks,
|
||||||
} from "../config";
|
} from "../config";
|
||||||
import { QlPackGenerator } from "../qlpack-generator";
|
import { QlPackGenerator } from "../qlpack-generator";
|
||||||
import { QueryLanguage } from "../common/query-language";
|
|
||||||
import { App } from "../common/app";
|
import { App } from "../common/app";
|
||||||
import { existsSync } from "fs";
|
import { existsSync } from "fs";
|
||||||
|
|
||||||
@@ -739,6 +739,13 @@ export class DatabaseManager extends DisposableObject {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isQueryLanguage(databaseItem.language)) {
|
||||||
|
void this.logger.log(
|
||||||
|
"Could not create skeleton QL pack because the selected database's language is not supported.",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const firstWorkspaceFolder = getFirstWorkspaceFolder();
|
const firstWorkspaceFolder = getFirstWorkspaceFolder();
|
||||||
const folderName = `codeql-custom-queries-${databaseItem.language}`;
|
const folderName = `codeql-custom-queries-${databaseItem.language}`;
|
||||||
|
|
||||||
@@ -769,7 +776,7 @@ export class DatabaseManager extends DisposableObject {
|
|||||||
try {
|
try {
|
||||||
const qlPackGenerator = new QlPackGenerator(
|
const qlPackGenerator = new QlPackGenerator(
|
||||||
folderName,
|
folderName,
|
||||||
databaseItem.language as QueryLanguage,
|
databaseItem.language,
|
||||||
this.cli,
|
this.cli,
|
||||||
firstWorkspaceFolder,
|
firstWorkspaceFolder,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ 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 { getQlPackPath } from "./pure/ql";
|
||||||
import { dbSchemeToLanguage } 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";
|
||||||
|
|
||||||
@@ -726,6 +726,10 @@ export async function isLikelyDbLanguageFolder(dbPath: string) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isQueryLanguage(language: string): language is QueryLanguage {
|
||||||
|
return Object.values(QueryLanguage).includes(language as QueryLanguage);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the language that a query targets.
|
* Finds the language that a query targets.
|
||||||
* If it can't be autodetected, prompt the user to specify the language manually.
|
* If it can't be autodetected, prompt the user to specify the language manually.
|
||||||
@@ -733,7 +737,7 @@ export async function isLikelyDbLanguageFolder(dbPath: string) {
|
|||||||
export async function findLanguage(
|
export async function findLanguage(
|
||||||
cliServer: CodeQLCliServer,
|
cliServer: CodeQLCliServer,
|
||||||
queryUri: Uri | undefined,
|
queryUri: Uri | undefined,
|
||||||
): Promise<string | undefined> {
|
): Promise<QueryLanguage | undefined> {
|
||||||
const uri = queryUri || Window.activeTextEditor?.document.uri;
|
const uri = queryUri || Window.activeTextEditor?.document.uri;
|
||||||
if (uri !== undefined) {
|
if (uri !== undefined) {
|
||||||
try {
|
try {
|
||||||
@@ -743,7 +747,14 @@ export async function findLanguage(
|
|||||||
);
|
);
|
||||||
const language = Object.keys(queryInfo.byLanguage)[0];
|
const language = Object.keys(queryInfo.byLanguage)[0];
|
||||||
void extLogger.log(`Detected query language: ${language}`);
|
void extLogger.log(`Detected query language: ${language}`);
|
||||||
return language;
|
|
||||||
|
if (isQueryLanguage(language)) {
|
||||||
|
return language;
|
||||||
|
}
|
||||||
|
|
||||||
|
void extLogger.log(
|
||||||
|
"Query language is unsupported. Select language manually.",
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
void extLogger.log(
|
void extLogger.log(
|
||||||
"Could not autodetect query language. Select language manually.",
|
"Could not autodetect query language. Select language manually.",
|
||||||
@@ -758,7 +769,7 @@ export async function findLanguage(
|
|||||||
export async function askForLanguage(
|
export async function askForLanguage(
|
||||||
cliServer: CodeQLCliServer,
|
cliServer: CodeQLCliServer,
|
||||||
throwOnEmpty = true,
|
throwOnEmpty = true,
|
||||||
): Promise<string | undefined> {
|
): Promise<QueryLanguage | undefined> {
|
||||||
const language = await Window.showQuickPick(
|
const language = await Window.showQuickPick(
|
||||||
await cliServer.getSupportedLanguages(),
|
await cliServer.getSupportedLanguages(),
|
||||||
{
|
{
|
||||||
@@ -775,7 +786,16 @@ export async function askForLanguage(
|
|||||||
"Language not found. Language must be specified manually.",
|
"Language not found. Language must be specified manually.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isQueryLanguage(language)) {
|
||||||
|
void showAndLogErrorMessage(
|
||||||
|
`Language '${language}' is not supported. Language must be specified manually.`,
|
||||||
|
);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
return language;
|
return language;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export const QUERY_LANGUAGE_TO_DATABASE_REPO: QueryLanguagesToDatabaseMap = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class SkeletonQueryWizard {
|
export class SkeletonQueryWizard {
|
||||||
private language: string | undefined;
|
private language: QueryLanguage | undefined;
|
||||||
private fileName = "example.ql";
|
private fileName = "example.ql";
|
||||||
private qlPackStoragePath: string | undefined;
|
private qlPackStoragePath: string | undefined;
|
||||||
|
|
||||||
@@ -154,6 +154,9 @@ export class SkeletonQueryWizard {
|
|||||||
if (this.folderName === undefined) {
|
if (this.folderName === undefined) {
|
||||||
throw new Error("Folder name is undefined");
|
throw new Error("Folder name is undefined");
|
||||||
}
|
}
|
||||||
|
if (this.language === undefined) {
|
||||||
|
throw new Error("Language is undefined");
|
||||||
|
}
|
||||||
|
|
||||||
this.progress({
|
this.progress({
|
||||||
message: "Creating skeleton QL pack around query",
|
message: "Creating skeleton QL pack around query",
|
||||||
@@ -164,7 +167,7 @@ export class SkeletonQueryWizard {
|
|||||||
try {
|
try {
|
||||||
const qlPackGenerator = new QlPackGenerator(
|
const qlPackGenerator = new QlPackGenerator(
|
||||||
this.folderName,
|
this.folderName,
|
||||||
this.language as QueryLanguage,
|
this.language,
|
||||||
this.cliServer,
|
this.cliServer,
|
||||||
this.qlPackStoragePath,
|
this.qlPackStoragePath,
|
||||||
);
|
);
|
||||||
@@ -181,6 +184,9 @@ export class SkeletonQueryWizard {
|
|||||||
if (this.folderName === undefined) {
|
if (this.folderName === undefined) {
|
||||||
throw new Error("Folder name is undefined");
|
throw new Error("Folder name is undefined");
|
||||||
}
|
}
|
||||||
|
if (this.language === undefined) {
|
||||||
|
throw new Error("Language is undefined");
|
||||||
|
}
|
||||||
|
|
||||||
this.progress({
|
this.progress({
|
||||||
message:
|
message:
|
||||||
@@ -192,7 +198,7 @@ export class SkeletonQueryWizard {
|
|||||||
try {
|
try {
|
||||||
const qlPackGenerator = new QlPackGenerator(
|
const qlPackGenerator = new QlPackGenerator(
|
||||||
this.folderName,
|
this.folderName,
|
||||||
this.language as QueryLanguage,
|
this.language,
|
||||||
this.cliServer,
|
this.cliServer,
|
||||||
this.qlPackStoragePath,
|
this.qlPackStoragePath,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import {
|
|||||||
QLPACK_FILENAMES,
|
QLPACK_FILENAMES,
|
||||||
QLPACK_LOCK_FILENAMES,
|
QLPACK_LOCK_FILENAMES,
|
||||||
} from "../pure/ql";
|
} from "../pure/ql";
|
||||||
|
import { QueryLanguage } from "../common/query-language";
|
||||||
|
|
||||||
export interface QlPack {
|
export interface QlPack {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -76,7 +77,7 @@ async function generateQueryPack(
|
|||||||
const targetQueryFileName = join(queryPackDir, packRelativePath);
|
const targetQueryFileName = join(queryPackDir, packRelativePath);
|
||||||
const workspaceFolders = getOnDiskWorkspaceFolders();
|
const workspaceFolders = getOnDiskWorkspaceFolders();
|
||||||
|
|
||||||
let language: string | undefined;
|
let language: QueryLanguage | undefined;
|
||||||
|
|
||||||
// Check if the query is already in a query pack.
|
// Check if the query is already in a query pack.
|
||||||
// If so, copy the entire query pack to the temporary directory.
|
// If so, copy the entire query pack to the temporary directory.
|
||||||
|
|||||||
Reference in New Issue
Block a user