Merge remote-tracking branch 'origin/main' into koesie10/rename-last-commit-to-last-updated
This commit is contained in:
@@ -49,7 +49,6 @@ const baseConfig = {
|
||||
"@typescript-eslint/no-throw-literal": "error",
|
||||
"no-useless-escape": 0,
|
||||
camelcase: "off",
|
||||
eqeqeq: "off",
|
||||
"escompat/no-regexp-lookbehind": "off",
|
||||
"etc/no-implicit-any-catch": "error",
|
||||
"filenames/match-regex": "off",
|
||||
|
||||
@@ -1483,7 +1483,7 @@
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "npm run format-staged",
|
||||
"pre-push": "npm run lint && scripts/forbid-test-only"
|
||||
"pre-push": "scripts/forbid-test-only"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
|
||||
@@ -51,7 +51,7 @@ export abstract class AbstractWebview<
|
||||
}
|
||||
|
||||
protected async getPanel(): Promise<WebviewPanel> {
|
||||
if (this.panel == undefined) {
|
||||
if (this.panel === undefined) {
|
||||
const { ctx } = this;
|
||||
|
||||
// This is an async method, so in theory this method can be called concurrently. To ensure that we don't
|
||||
|
||||
@@ -25,7 +25,9 @@ import {
|
||||
} from "./pure/bqrs-utils";
|
||||
import { commandRunner } from "./commandRunner";
|
||||
import { DisposableObject } from "./pure/disposable-object";
|
||||
import { showAndLogErrorMessage } from "./helpers";
|
||||
import { showAndLogExceptionWithTelemetry } from "./helpers";
|
||||
import { asError, getErrorMessage } from "./pure/helpers-pure";
|
||||
import { redactableError } from "./pure/errors";
|
||||
|
||||
export interface AstItem {
|
||||
id: BqrsId;
|
||||
@@ -146,7 +148,12 @@ export class AstViewer extends DisposableObject {
|
||||
() => {
|
||||
/**/
|
||||
},
|
||||
(err) => showAndLogErrorMessage(err),
|
||||
(error: unknown) =>
|
||||
showAndLogExceptionWithTelemetry(
|
||||
redactableError(
|
||||
asError(error),
|
||||
)`Failed to reveal AST: ${getErrorMessage(error)}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -204,7 +211,12 @@ export class AstViewer extends DisposableObject {
|
||||
() => {
|
||||
/**/
|
||||
},
|
||||
(err) => showAndLogErrorMessage(err),
|
||||
(error: unknown) =>
|
||||
showAndLogExceptionWithTelemetry(
|
||||
redactableError(
|
||||
asError(error),
|
||||
)`Failed to reveal AST: ${getErrorMessage(error)}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,7 +384,7 @@ export class CodeQLCliServer implements Disposable {
|
||||
this.killProcessIfRunning();
|
||||
// Report the error (if there is a stderr then use that otherwise just report the error cod or nodejs error)
|
||||
const newError =
|
||||
stderrBuffers.length == 0
|
||||
stderrBuffers.length === 0
|
||||
? new Error(`${description} failed: ${err}`)
|
||||
: new Error(
|
||||
`${description} failed: ${Buffer.concat(stderrBuffers).toString(
|
||||
|
||||
@@ -6,10 +6,14 @@ import {
|
||||
Disposable,
|
||||
ProgressLocation,
|
||||
} from "vscode";
|
||||
import { showAndLogErrorMessage, showAndLogWarningMessage } from "./helpers";
|
||||
import {
|
||||
showAndLogExceptionWithTelemetry,
|
||||
showAndLogWarningMessage,
|
||||
} from "./helpers";
|
||||
import { extLogger } from "./common";
|
||||
import { asError, getErrorMessage, getErrorStack } from "./pure/helpers-pure";
|
||||
import { telemetryListener } from "./telemetry";
|
||||
import { redactableError } from "./pure/errors";
|
||||
|
||||
export class UserCancellationException extends Error {
|
||||
/**
|
||||
@@ -125,23 +129,28 @@ export function commandRunner(
|
||||
try {
|
||||
return await task(...args);
|
||||
} catch (e) {
|
||||
const errorMessage = `${getErrorMessage(e) || e} (${commandId})`;
|
||||
error = asError(e);
|
||||
const errorMessage = redactableError(error)`${
|
||||
getErrorMessage(e) || e
|
||||
} (${commandId})`;
|
||||
const errorStack = getErrorStack(e);
|
||||
if (e instanceof UserCancellationException) {
|
||||
// User has cancelled this action manually
|
||||
if (e.silent) {
|
||||
void extLogger.log(errorMessage);
|
||||
void extLogger.log(errorMessage.fullMessage);
|
||||
} else {
|
||||
void showAndLogWarningMessage(errorMessage);
|
||||
void showAndLogWarningMessage(errorMessage.fullMessage);
|
||||
}
|
||||
} else {
|
||||
// Include the full stack in the error log only.
|
||||
const fullMessage = errorStack
|
||||
? `${errorMessage}\n${errorStack}`
|
||||
: errorMessage;
|
||||
void showAndLogErrorMessage(errorMessage, {
|
||||
? `${errorMessage.fullMessage}\n${errorStack}`
|
||||
: errorMessage.fullMessage;
|
||||
void showAndLogExceptionWithTelemetry(errorMessage, {
|
||||
fullMessage,
|
||||
extraTelemetryProperties: {
|
||||
command: commandId,
|
||||
},
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
@@ -178,24 +187,31 @@ export function commandRunnerWithProgress<R>(
|
||||
try {
|
||||
return await withProgress(progressOptionsWithDefaults, task, ...args);
|
||||
} catch (e) {
|
||||
const errorMessage = `${getErrorMessage(e) || e} (${commandId})`;
|
||||
error = asError(e);
|
||||
const errorMessage = redactableError`${
|
||||
getErrorMessage(e) || e
|
||||
} (${commandId})`;
|
||||
const errorStack = getErrorStack(e);
|
||||
if (e instanceof UserCancellationException) {
|
||||
// User has cancelled this action manually
|
||||
if (e.silent) {
|
||||
void outputLogger.log(errorMessage);
|
||||
void outputLogger.log(errorMessage.fullMessage);
|
||||
} else {
|
||||
void showAndLogWarningMessage(errorMessage, { outputLogger });
|
||||
void showAndLogWarningMessage(errorMessage.fullMessage, {
|
||||
outputLogger,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Include the full stack in the error log only.
|
||||
const fullMessage = errorStack
|
||||
? `${errorMessage}\n${errorStack}`
|
||||
: errorMessage;
|
||||
void showAndLogErrorMessage(errorMessage, {
|
||||
? `${errorMessage.fullMessage}\n${errorStack}`
|
||||
: errorMessage.fullMessage;
|
||||
void showAndLogExceptionWithTelemetry(errorMessage, {
|
||||
outputLogger,
|
||||
fullMessage,
|
||||
extraTelemetryProperties: {
|
||||
command: commandId,
|
||||
},
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
|
||||
@@ -357,7 +357,7 @@ export class QueryServerConfigListener
|
||||
if (memory === null) {
|
||||
return undefined;
|
||||
}
|
||||
if (memory == 0 || typeof memory !== "number") {
|
||||
if (memory === 0 || typeof memory !== "number") {
|
||||
void extLogger.log(
|
||||
`Ignoring value '${memory}' for setting ${MEMORY_SETTING.qualifiedName}`,
|
||||
);
|
||||
|
||||
@@ -128,9 +128,9 @@ function createTemplates(path: string): Record<string, string> {
|
||||
function isValidSelect(selectInfo: ResultSetSchema | undefined) {
|
||||
return (
|
||||
selectInfo &&
|
||||
selectInfo.columns.length == 3 &&
|
||||
selectInfo.columns[0].kind == ColumnKindCode.ENTITY &&
|
||||
selectInfo.columns[1].kind == ColumnKindCode.ENTITY &&
|
||||
selectInfo.columns[2].kind == ColumnKindCode.STRING
|
||||
selectInfo.columns.length === 3 &&
|
||||
selectInfo.columns[0].kind === ColumnKindCode.ENTITY &&
|
||||
selectInfo.columns[1].kind === ColumnKindCode.ENTITY &&
|
||||
selectInfo.columns[2].kind === ColumnKindCode.STRING
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
getPrimaryDbscheme,
|
||||
getQlPackForDbscheme,
|
||||
getOnDiskWorkspaceFolders,
|
||||
showAndLogErrorMessage,
|
||||
QlPacksForLanguage,
|
||||
showAndLogExceptionWithTelemetry,
|
||||
} from "../helpers";
|
||||
import { KeyType, kindOfKeyType, nameOfKeyType, tagOfKeyType } from "./keyType";
|
||||
import { CodeQLCliServer } from "../cli";
|
||||
@@ -18,6 +18,7 @@ import { createInitialQueryInfo } from "../run-queries-shared";
|
||||
import { CancellationToken, Uri } from "vscode";
|
||||
import { ProgressCallback } from "../commandRunner";
|
||||
import { QueryRunner } from "../queryRunner";
|
||||
import { redactableError } from "../pure/errors";
|
||||
|
||||
export async function qlpackOfDatabase(
|
||||
cli: CodeQLCliServer,
|
||||
@@ -88,22 +89,16 @@ export async function resolveQueries(
|
||||
}
|
||||
|
||||
// No queries found. Determine the correct error message for the various scenarios.
|
||||
const errorMessage = `No ${nameOfKeyType(
|
||||
keyType,
|
||||
)} queries (tagged "${tagOfKeyType(
|
||||
keyType,
|
||||
)}") could be found in the current library path. \
|
||||
Try upgrading the CodeQL libraries. If that doesn't work, then ${nameOfKeyType(
|
||||
keyType,
|
||||
)} queries are not yet available \
|
||||
for this language.`;
|
||||
const keyTypeName = nameOfKeyType(keyType);
|
||||
const keyTypeTag = tagOfKeyType(keyType);
|
||||
const joinedPacksToSearch = packsToSearch.join(", ");
|
||||
const error = redactableError`No ${keyTypeName} queries (tagged "${keyTypeTag}") could be found in the \
|
||||
current library path (tried searching the following packs: ${joinedPacksToSearch}). \
|
||||
Try upgrading the CodeQL libraries. If that doesn't work, then ${keyTypeName} queries are not yet available \
|
||||
for this language.`;
|
||||
|
||||
void showAndLogErrorMessage(errorMessage);
|
||||
throw new Error(
|
||||
`Couldn't find any queries tagged ${tagOfKeyType(
|
||||
keyType,
|
||||
)} in any of the following packs: ${packsToSearch.join(", ")}.`,
|
||||
);
|
||||
void showAndLogExceptionWithTelemetry(error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
async function resolveContextualQuery(
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
isLikelyDatabaseRoot,
|
||||
isLikelyDbLanguageFolder,
|
||||
showAndLogErrorMessage,
|
||||
showAndLogExceptionWithTelemetry,
|
||||
} from "./helpers";
|
||||
import { extLogger } from "./common";
|
||||
import {
|
||||
@@ -37,11 +38,12 @@ import {
|
||||
promptImportGithubDatabase,
|
||||
promptImportInternetDatabase,
|
||||
} from "./databaseFetcher";
|
||||
import { asyncFilter, getErrorMessage } from "./pure/helpers-pure";
|
||||
import { asError, asyncFilter, getErrorMessage } from "./pure/helpers-pure";
|
||||
import { QueryRunner } from "./queryRunner";
|
||||
import { isCanary } from "./config";
|
||||
import { App } from "./common/app";
|
||||
import { Credentials } from "./common/authentication";
|
||||
import { redactableError } from "./pure/errors";
|
||||
|
||||
enum SortOrder {
|
||||
NameAsc = "NameAsc",
|
||||
@@ -354,7 +356,11 @@ export class DatabaseUI extends DisposableObject {
|
||||
try {
|
||||
await this.chooseAndSetDatabase(true, progress, token);
|
||||
} catch (e) {
|
||||
void showAndLogErrorMessage(getErrorMessage(e));
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError(
|
||||
asError(e),
|
||||
)`Failed to choose and set database: ${getErrorMessage(e)}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -437,6 +443,11 @@ export class DatabaseUI extends DisposableObject {
|
||||
void extLogger.log(`Deleting orphaned database '${dbDir}'.`);
|
||||
await remove(dbDir);
|
||||
} catch (e) {
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError(
|
||||
asError(e),
|
||||
)`Failed to delete orphaned database: ${getErrorMessage(e)}`,
|
||||
);
|
||||
failures.push(`${basename(dbDir)}`);
|
||||
}
|
||||
}),
|
||||
@@ -458,8 +469,12 @@ export class DatabaseUI extends DisposableObject {
|
||||
): Promise<void> => {
|
||||
try {
|
||||
await this.chooseAndSetDatabase(false, progress, token);
|
||||
} catch (e) {
|
||||
void showAndLogErrorMessage(getErrorMessage(e));
|
||||
} catch (e: unknown) {
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError(
|
||||
asError(e),
|
||||
)`Failed to choose and set database: ${getErrorMessage(e)}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@ import * as vscode from "vscode";
|
||||
import * as cli from "./cli";
|
||||
import { ExtensionContext } from "vscode";
|
||||
import {
|
||||
showAndLogErrorMessage,
|
||||
showAndLogWarningMessage,
|
||||
showAndLogInformationMessage,
|
||||
isLikelyDatabaseRoot,
|
||||
showAndLogExceptionWithTelemetry,
|
||||
} from "./helpers";
|
||||
import { ProgressCallback, withProgress } from "./commandRunner";
|
||||
import {
|
||||
@@ -22,6 +22,7 @@ import { Logger, extLogger } from "./common";
|
||||
import { asError, getErrorMessage } from "./pure/helpers-pure";
|
||||
import { QueryRunner } from "./queryRunner";
|
||||
import { pathsEqual } from "./pure/files";
|
||||
import { redactableError } from "./pure/errors";
|
||||
|
||||
/**
|
||||
* databases.ts
|
||||
@@ -794,8 +795,10 @@ export class DatabaseManager extends DisposableObject {
|
||||
await this.updatePersistedDatabaseList();
|
||||
} catch (e) {
|
||||
// database list had an unexpected type - nothing to be done?
|
||||
void showAndLogErrorMessage(
|
||||
`Database list loading failed: ${getErrorMessage(e)}`,
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError(
|
||||
asError(e),
|
||||
)`Database list loading failed: ${getErrorMessage(e)}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -903,7 +906,7 @@ export class DatabaseManager extends DisposableObject {
|
||||
token: vscode.CancellationToken,
|
||||
item: DatabaseItem,
|
||||
) {
|
||||
if (this._currentDatabaseItem == item) {
|
||||
if (this._currentDatabaseItem === item) {
|
||||
this._currentDatabaseItem = undefined;
|
||||
}
|
||||
const index = this.databaseItems.findIndex(
|
||||
|
||||
@@ -650,10 +650,10 @@ export class ReleasesApiConsumer {
|
||||
redirectCount < ReleasesApiConsumer._maxRedirects
|
||||
) {
|
||||
const parsedRedirectUrl = parse(redirectUrl);
|
||||
if (parsedRedirectUrl.protocol != "https:") {
|
||||
if (parsedRedirectUrl.protocol !== "https:") {
|
||||
throw new Error("Encountered a non-https redirect, rejecting");
|
||||
}
|
||||
if (parsedRedirectUrl.host != "api.github.com") {
|
||||
if (parsedRedirectUrl.host !== "api.github.com") {
|
||||
// Remove authorization header if we are redirected outside of the GitHub API.
|
||||
//
|
||||
// This is necessary to stream release assets since AWS fails if more than one auth
|
||||
|
||||
@@ -10,7 +10,9 @@ import {
|
||||
} from "vscode";
|
||||
import { commandRunner } from "./commandRunner";
|
||||
import { DisposableObject } from "./pure/disposable-object";
|
||||
import { showAndLogErrorMessage } from "./helpers";
|
||||
import { showAndLogExceptionWithTelemetry } from "./helpers";
|
||||
import { asError, getErrorMessage } from "./pure/helpers-pure";
|
||||
import { redactableError } from "./pure/errors";
|
||||
|
||||
export interface EvalLogTreeItem {
|
||||
label?: string;
|
||||
@@ -104,7 +106,12 @@ export class EvalLogViewer extends DisposableObject {
|
||||
() => {
|
||||
/**/
|
||||
},
|
||||
(err) => showAndLogErrorMessage(err),
|
||||
(err: unknown) =>
|
||||
showAndLogExceptionWithTelemetry(
|
||||
redactableError(
|
||||
asError(err),
|
||||
)`Failed to reveal tree view: ${getErrorMessage(err)}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ import {
|
||||
showAndLogInformationMessage,
|
||||
showInformationMessageWithAction,
|
||||
tmpDir,
|
||||
showAndLogExceptionWithTelemetry,
|
||||
} from "./helpers";
|
||||
import { asError, assertNever, getErrorMessage } from "./pure/helpers-pure";
|
||||
import { spawnIdeServer } from "./ide-server";
|
||||
@@ -137,6 +138,7 @@ import { VariantAnalysisResultsManager } from "./remote-queries/variant-analysis
|
||||
import { ExtensionApp } from "./common/vscode/vscode-app";
|
||||
import { RepositoriesFilterSortStateWithIds } from "./pure/variant-analysis-filter-sort";
|
||||
import { DbModule } from "./databases/db-module";
|
||||
import { redactableError } from "./pure/errors";
|
||||
|
||||
/**
|
||||
* extension.ts
|
||||
@@ -713,7 +715,11 @@ async function activateWithInstalledDistribution(
|
||||
try {
|
||||
await compareView.showResults(from, to);
|
||||
} catch (e) {
|
||||
void showAndLogErrorMessage(getErrorMessage(e));
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError(asError(e))`Failed to show results: ${getErrorMessage(
|
||||
e,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -809,10 +815,10 @@ async function activateWithInstalledDistribution(
|
||||
const errorMessage = getErrorMessage(e).includes(
|
||||
"Generating qhelp in markdown",
|
||||
)
|
||||
? `Could not generate markdown from ${pathToQhelp}: Bad formatting in .qhelp file.`
|
||||
: `Could not open a preview of the generated file (${absolutePathToMd}).`;
|
||||
void showAndLogErrorMessage(errorMessage, {
|
||||
fullMessage: `${errorMessage}\n${e}`,
|
||||
? redactableError`Could not generate markdown from ${pathToQhelp}: Bad formatting in .qhelp file.`
|
||||
: redactableError`Could not open a preview of the generated file (${absolutePathToMd}).`;
|
||||
void showAndLogExceptionWithTelemetry(errorMessage, {
|
||||
fullMessage: `${errorMessage}\n${getErrorMessage(e)}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import { CodeQLCliServer, QlpacksInfo } from "./cli";
|
||||
import { UserCancellationException } from "./commandRunner";
|
||||
import { extLogger, OutputChannelLogger } from "./common";
|
||||
import { QueryMetadata } from "./pure/interface-types";
|
||||
import { telemetryListener } from "./telemetry";
|
||||
import { RedactableError } from "./pure/errors";
|
||||
|
||||
// Shared temporary folder for the extension.
|
||||
export const tmpDir = dirSync({
|
||||
@@ -43,6 +45,11 @@ export const tmpDirDisposal = {
|
||||
},
|
||||
};
|
||||
|
||||
interface ShowAndLogExceptionOptions extends ShowAndLogOptions {
|
||||
/** Custom properties to include in the telemetry report. */
|
||||
extraTelemetryProperties?: { [key: string]: string };
|
||||
}
|
||||
|
||||
interface ShowAndLogOptions {
|
||||
/** The output logger that will receive the message. */
|
||||
outputLogger?: OutputChannelLogger;
|
||||
@@ -55,11 +62,27 @@ interface ShowAndLogOptions {
|
||||
fullMessage?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show an error message, log it to the console, and emit redacted information as telemetry
|
||||
*
|
||||
* @param error The error to show. Only redacted information will be included in the telemetry.
|
||||
* @param options See individual fields on `ShowAndLogExceptionOptions` type.
|
||||
*
|
||||
* @return A promise that resolves to the selected item or undefined when being dismissed.
|
||||
*/
|
||||
export async function showAndLogExceptionWithTelemetry(
|
||||
error: RedactableError,
|
||||
options: ShowAndLogExceptionOptions = {},
|
||||
): Promise<string | undefined> {
|
||||
telemetryListener?.sendError(error, options.extraTelemetryProperties);
|
||||
return showAndLogErrorMessage(error.fullMessage, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show an error message and log it to the console
|
||||
*
|
||||
* @param message The message to show.
|
||||
* @param options See indivual fields on `ShowAndLogOptions` type.
|
||||
* @param options See individual fields on `ShowAndLogOptions` type.
|
||||
*
|
||||
* @return A promise that resolves to the selected item or undefined when being dismissed.
|
||||
*/
|
||||
@@ -82,7 +105,7 @@ function dropLinesExceptInitial(message: string, n = 2) {
|
||||
* Show a warning message and log it to the console
|
||||
*
|
||||
* @param message The message to show.
|
||||
* @param options See indivual fields on `ShowAndLogOptions` type.
|
||||
* @param options See individual fields on `ShowAndLogOptions` type.
|
||||
*
|
||||
* @return A promise that resolves to the selected item or undefined when being dismissed.
|
||||
*/
|
||||
@@ -97,7 +120,7 @@ export async function showAndLogWarningMessage(
|
||||
* Show an information message and log it to the console
|
||||
*
|
||||
* @param message The message to show.
|
||||
* @param options See indivual fields on `ShowAndLogOptions` type.
|
||||
* @param options See individual fields on `ShowAndLogOptions` type.
|
||||
*
|
||||
* @return A promise that resolves to the selected item or undefined when being dismissed.
|
||||
*/
|
||||
|
||||
@@ -13,8 +13,9 @@ import {
|
||||
import * as cli from "./cli";
|
||||
import { CodeQLCliServer } from "./cli";
|
||||
import { DatabaseEventKind, DatabaseItem, DatabaseManager } from "./databases";
|
||||
import { showAndLogErrorMessage } from "./helpers";
|
||||
import { showAndLogExceptionWithTelemetry } from "./helpers";
|
||||
import {
|
||||
asError,
|
||||
assertNever,
|
||||
getErrorMessage,
|
||||
getErrorStack,
|
||||
@@ -66,6 +67,7 @@ import { AbstractWebview, WebviewPanelConfig } from "./abstract-webview";
|
||||
import { PAGE_SIZE } from "./config";
|
||||
import { HistoryItemLabelProvider } from "./query-history/history-item-label-provider";
|
||||
import { telemetryListener } from "./telemetry";
|
||||
import { redactableError } from "./pure/errors";
|
||||
|
||||
/**
|
||||
* interface.ts
|
||||
@@ -110,7 +112,7 @@ function sortInterpretedResults(
|
||||
function interpretedPageSize(
|
||||
interpretation: Interpretation | undefined,
|
||||
): number {
|
||||
if (interpretation?.data.t == "GraphInterpretationData") {
|
||||
if (interpretation?.data.t === "GraphInterpretationData") {
|
||||
// Graph views always have one result per page.
|
||||
return 1;
|
||||
}
|
||||
@@ -124,7 +126,7 @@ function numPagesOfResultSet(
|
||||
const pageSize = interpretedPageSize(interpretation);
|
||||
|
||||
const n =
|
||||
interpretation?.data.t == "GraphInterpretationData"
|
||||
interpretation?.data.t === "GraphInterpretationData"
|
||||
? interpretation.data.dot.length
|
||||
: resultSet.schema.rows;
|
||||
|
||||
@@ -141,7 +143,7 @@ function numInterpretedPages(
|
||||
const pageSize = interpretedPageSize(interpretation);
|
||||
|
||||
const n =
|
||||
interpretation.data.t == "GraphInterpretationData"
|
||||
interpretation.data.t === "GraphInterpretationData"
|
||||
? interpretation.data.dot.length
|
||||
: interpretation.data.runs[0].results?.length || 0;
|
||||
|
||||
@@ -291,9 +293,14 @@ export class ResultsView extends AbstractWebview<
|
||||
assertNever(msg);
|
||||
}
|
||||
} catch (e) {
|
||||
void showAndLogErrorMessage(getErrorMessage(e), {
|
||||
fullMessage: getErrorStack(e),
|
||||
});
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError(
|
||||
asError(e),
|
||||
)`Error handling message from results view: ${getErrorMessage(e)}`,
|
||||
{
|
||||
fullMessage: getErrorStack(e),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,8 +342,8 @@ export class ResultsView extends AbstractWebview<
|
||||
sortState: InterpretedResultsSortState | undefined,
|
||||
): Promise<void> {
|
||||
if (this._displayedQuery === undefined) {
|
||||
void showAndLogErrorMessage(
|
||||
"Failed to sort results since evaluation info was unknown.",
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError`Failed to sort results since evaluation info was unknown.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -353,8 +360,8 @@ export class ResultsView extends AbstractWebview<
|
||||
sortState: RawResultsSortState | undefined,
|
||||
): Promise<void> {
|
||||
if (this._displayedQuery === undefined) {
|
||||
void showAndLogErrorMessage(
|
||||
"Failed to sort results since evaluation info was unknown.",
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError`Failed to sort results since evaluation info was unknown.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -446,7 +453,7 @@ export class ResultsView extends AbstractWebview<
|
||||
|
||||
const selectedTable = getDefaultResultSetName(resultSetNames);
|
||||
const schema = resultSetSchemas.find(
|
||||
(resultSet) => resultSet.name == selectedTable,
|
||||
(resultSet) => resultSet.name === selectedTable,
|
||||
)!;
|
||||
|
||||
// Use sorted results path if it exists. This may happen if we are
|
||||
@@ -590,7 +597,7 @@ export class ResultsView extends AbstractWebview<
|
||||
const resultSetNames = allResultSetSchemas.map((schema) => schema.name);
|
||||
|
||||
const schema = resultSetSchemas.find(
|
||||
(resultSet) => resultSet.name == selectedTable,
|
||||
(resultSet) => resultSet.name === selectedTable,
|
||||
)!;
|
||||
if (schema === undefined)
|
||||
throw new Error(`Query result set '${selectedTable}' not found.`);
|
||||
@@ -762,8 +769,10 @@ export class ResultsView extends AbstractWebview<
|
||||
} catch (e) {
|
||||
// If interpretation fails, accept the error and continue
|
||||
// trying to render uninterpreted results anyway.
|
||||
void showAndLogErrorMessage(
|
||||
`Showing raw results instead of interpreted ones due to an error. ${getErrorMessage(
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError(
|
||||
asError(e),
|
||||
)`Showing raw results instead of interpreted ones due to an error. ${getErrorMessage(
|
||||
e,
|
||||
)}`,
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { DatabaseItem } from "../databases";
|
||||
import {
|
||||
getOnDiskWorkspaceFolders,
|
||||
showAndLogErrorMessage,
|
||||
showAndLogExceptionWithTelemetry,
|
||||
showAndLogWarningMessage,
|
||||
tryGetQueryMetadata,
|
||||
upgradesTmpDir,
|
||||
@@ -18,9 +19,10 @@ import { extLogger } from "../common";
|
||||
import * as messages from "../pure/legacy-messages";
|
||||
import { InitialQueryInfo, LocalQueryInfo } from "../query-results";
|
||||
import * as qsClient from "./queryserver-client";
|
||||
import { getErrorMessage } from "../pure/helpers-pure";
|
||||
import { asError, getErrorMessage } from "../pure/helpers-pure";
|
||||
import { compileDatabaseUpgradeSequence } from "./upgrades";
|
||||
import { QueryEvaluationInfo, QueryWithResults } from "../run-queries-shared";
|
||||
import { redactableError } from "../pure/errors";
|
||||
|
||||
/**
|
||||
* A collection of evaluation-time information about a query,
|
||||
@@ -321,7 +323,7 @@ export async function compileAndRunQueryAgainstDatabase(
|
||||
// This test will produce confusing results if we ever change the name of the database schema files.
|
||||
const querySchemaName = basename(packConfig.dbscheme);
|
||||
const dbSchemaName = basename(dbItem.contents.dbSchemeUri.fsPath);
|
||||
if (querySchemaName != dbSchemaName) {
|
||||
if (querySchemaName !== dbSchemaName) {
|
||||
void extLogger.log(
|
||||
`Query schema was ${querySchemaName}, but database schema was ${dbSchemaName}.`,
|
||||
);
|
||||
@@ -367,10 +369,11 @@ export async function compileAndRunQueryAgainstDatabase(
|
||||
void extLogger.log("Did not find any available ML models.");
|
||||
}
|
||||
} catch (e) {
|
||||
const message =
|
||||
`Couldn't resolve available ML models for ${qlProgram.queryPath}. Running the ` +
|
||||
`query without any ML models: ${e}.`;
|
||||
void showAndLogErrorMessage(message);
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError(
|
||||
asError(e),
|
||||
)`Couldn't resolve available ML models for ${qlProgram.queryPath}. Running the query without any ML models: ${e}.`,
|
||||
);
|
||||
}
|
||||
|
||||
const hasMetadataFile = await dbItem.hasMetadataFile();
|
||||
@@ -403,7 +406,7 @@ export async function compileAndRunQueryAgainstDatabase(
|
||||
} catch (e) {
|
||||
if (
|
||||
e instanceof ResponseError &&
|
||||
e.code == LSPErrorCodes.RequestCancelled
|
||||
e.code === LSPErrorCodes.RequestCancelled
|
||||
) {
|
||||
return createSyntheticResult(query, "Query cancelled");
|
||||
} else {
|
||||
@@ -422,9 +425,11 @@ export async function compileAndRunQueryAgainstDatabase(
|
||||
queryInfo,
|
||||
);
|
||||
if (result.resultType !== messages.QueryResultType.SUCCESS) {
|
||||
const message = result.message || "Failed to run query";
|
||||
void extLogger.log(message);
|
||||
void showAndLogErrorMessage(message);
|
||||
const error = result.message
|
||||
? redactableError`${result.message}`
|
||||
: redactableError`Failed to run query`;
|
||||
void extLogger.log(error.fullMessage);
|
||||
void showAndLogExceptionWithTelemetry(error);
|
||||
}
|
||||
const message = formatLegacyMessage(result);
|
||||
|
||||
@@ -432,7 +437,7 @@ export async function compileAndRunQueryAgainstDatabase(
|
||||
query: query.queryEvalInfo,
|
||||
message,
|
||||
result,
|
||||
successful: result.resultType == messages.QueryResultType.SUCCESS,
|
||||
successful: result.resultType === messages.QueryResultType.SUCCESS,
|
||||
logFileLocation: result.logFileLocation,
|
||||
dispose: () => {
|
||||
qs.logger.removeAdditionalLogLocation(result.logFileLocation);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as vscode from "vscode";
|
||||
import {
|
||||
getOnDiskWorkspaceFolders,
|
||||
showAndLogErrorMessage,
|
||||
showAndLogExceptionWithTelemetry,
|
||||
tmpDir,
|
||||
} from "../helpers";
|
||||
import { ProgressCallback, UserCancellationException } from "../commandRunner";
|
||||
@@ -11,6 +11,8 @@ import * as qsClient from "./queryserver-client";
|
||||
import * as tmp from "tmp-promise";
|
||||
import { dirname } from "path";
|
||||
import { DatabaseItem } from "../databases";
|
||||
import { asError, getErrorMessage } from "../pure/helpers-pure";
|
||||
import { redactableError } from "../pure/errors";
|
||||
|
||||
/**
|
||||
* Maximum number of lines to include from database upgrade message,
|
||||
@@ -209,8 +211,10 @@ export async function upgradeDatabaseExplicit(
|
||||
token,
|
||||
);
|
||||
} catch (e) {
|
||||
void showAndLogErrorMessage(
|
||||
`Compilation of database upgrades failed: ${e}`,
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError(
|
||||
asError(e),
|
||||
)`Compilation of database upgrades failed: ${getErrorMessage(e)}`,
|
||||
);
|
||||
return;
|
||||
} finally {
|
||||
@@ -218,10 +222,11 @@ export async function upgradeDatabaseExplicit(
|
||||
}
|
||||
|
||||
if (!compileUpgradeResult.compiledUpgrades) {
|
||||
const error =
|
||||
compileUpgradeResult.error || "[no error message available]";
|
||||
void showAndLogErrorMessage(
|
||||
`Compilation of database upgrades failed: ${error}`,
|
||||
const error = compileUpgradeResult.error
|
||||
? redactableError`${compileUpgradeResult.error}`
|
||||
: redactableError`[no error message available]`;
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError`Compilation of database upgrades failed: ${error}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -253,7 +258,11 @@ export async function upgradeDatabaseExplicit(
|
||||
await qs.restartQueryServer(progress, token);
|
||||
return result;
|
||||
} catch (e) {
|
||||
void showAndLogErrorMessage(`Database upgrade failed: ${e}`);
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError(asError(e))`Database upgrade failed: ${getErrorMessage(
|
||||
e,
|
||||
)}`,
|
||||
);
|
||||
return;
|
||||
} finally {
|
||||
void qs.logger.log("Done running database upgrade.");
|
||||
|
||||
@@ -338,7 +338,7 @@ class JoinOrderScanner implements EvaluationLogScanner {
|
||||
inLayerEvent.predicateIterationMillis.length <= iteration
|
||||
? -1
|
||||
: inLayerEvent.predicateIterationMillis[iteration];
|
||||
if (iterationTime != -1) {
|
||||
if (iterationTime !== -1) {
|
||||
const run: PipelineRun =
|
||||
inLayerEvent.pipelineRuns[nextPipeline[predicate]++];
|
||||
func(inLayerEvent, run, iteration);
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { CodeQLCliServer } from "./cli";
|
||||
import {
|
||||
getOnDiskWorkspaceFolders,
|
||||
showAndLogErrorMessage,
|
||||
showAndLogExceptionWithTelemetry,
|
||||
showAndLogInformationMessage,
|
||||
} from "./helpers";
|
||||
import { QuickPickItem, window } from "vscode";
|
||||
import { ProgressCallback, UserCancellationException } from "./commandRunner";
|
||||
import { extLogger } from "./common";
|
||||
import { asError, getErrorStack } from "./pure/helpers-pure";
|
||||
import { redactableError } from "./pure/errors";
|
||||
|
||||
const QUERY_PACKS = [
|
||||
"codeql/cpp-queries",
|
||||
@@ -66,8 +68,13 @@ export async function handleDownloadPacks(
|
||||
await cliServer.packDownload(packsToDownload);
|
||||
void showAndLogInformationMessage("Finished downloading packs.");
|
||||
} catch (error) {
|
||||
void showAndLogErrorMessage(
|
||||
"Unable to download all packs. See log for more details.",
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError(
|
||||
asError(error),
|
||||
)`Unable to download all packs. See log for more details.`,
|
||||
{
|
||||
fullMessage: getErrorStack(error),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
72
extensions/ql-vscode/src/pure/errors.ts
Normal file
72
extensions/ql-vscode/src/pure/errors.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
export class RedactableError extends Error {
|
||||
constructor(
|
||||
cause: Error | undefined,
|
||||
private readonly strings: TemplateStringsArray,
|
||||
private readonly values: unknown[],
|
||||
) {
|
||||
super();
|
||||
|
||||
this.message = this.fullMessage;
|
||||
if (cause !== undefined) {
|
||||
this.stack = cause.stack;
|
||||
}
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return this.fullMessage;
|
||||
}
|
||||
|
||||
public get fullMessage(): string {
|
||||
return this.strings
|
||||
.map((s, i) => s + (this.hasValue(i) ? this.getValue(i) : ""))
|
||||
.join("");
|
||||
}
|
||||
|
||||
public get redactedMessage(): string {
|
||||
return this.strings
|
||||
.map((s, i) => s + (this.hasValue(i) ? this.getRedactedValue(i) : ""))
|
||||
.join("");
|
||||
}
|
||||
|
||||
private getValue(index: number): unknown {
|
||||
const value = this.values[index];
|
||||
if (value instanceof RedactableError) {
|
||||
return value.fullMessage;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private getRedactedValue(index: number): unknown {
|
||||
const value = this.values[index];
|
||||
if (value instanceof RedactableError) {
|
||||
return value.redactedMessage;
|
||||
}
|
||||
return "[REDACTED]";
|
||||
}
|
||||
|
||||
private hasValue(index: number): boolean {
|
||||
return index < this.values.length;
|
||||
}
|
||||
}
|
||||
|
||||
export function redactableError(
|
||||
strings: TemplateStringsArray,
|
||||
...values: unknown[]
|
||||
): RedactableError;
|
||||
export function redactableError(
|
||||
error: Error,
|
||||
): (strings: TemplateStringsArray, ...values: unknown[]) => RedactableError;
|
||||
|
||||
export function redactableError(
|
||||
errorOrStrings: Error | TemplateStringsArray,
|
||||
...values: unknown[]
|
||||
):
|
||||
| ((strings: TemplateStringsArray, ...values: unknown[]) => RedactableError)
|
||||
| RedactableError {
|
||||
if (errorOrStrings instanceof Error) {
|
||||
return (strings: TemplateStringsArray, ...values: unknown[]) =>
|
||||
new RedactableError(errorOrStrings, strings, values);
|
||||
} else {
|
||||
return new RedactableError(undefined, errorOrStrings, values);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
* Helper functions that don't depend on vscode or the CLI and therefore can be used by the front-end and pure unit tests.
|
||||
*/
|
||||
|
||||
import { RedactableError } from "./errors";
|
||||
|
||||
/**
|
||||
* This error is used to indicate a runtime failure of an exhaustivity check enforced at compile time.
|
||||
*/
|
||||
@@ -46,14 +48,22 @@ export const REPO_REGEX = /^[a-zA-Z0-9-_\.]+\/[a-zA-Z0-9-_\.]+$/;
|
||||
*/
|
||||
export const OWNER_REGEX = /^[a-zA-Z0-9-_\.]+$/;
|
||||
|
||||
export function getErrorMessage(e: unknown) {
|
||||
export function getErrorMessage(e: unknown): string {
|
||||
if (e instanceof RedactableError) {
|
||||
return e.fullMessage;
|
||||
}
|
||||
|
||||
return e instanceof Error ? e.message : String(e);
|
||||
}
|
||||
|
||||
export function getErrorStack(e: unknown) {
|
||||
export function getErrorStack(e: unknown): string {
|
||||
return e instanceof Error ? e.stack ?? "" : "";
|
||||
}
|
||||
|
||||
export function asError(e: unknown): Error {
|
||||
if (e instanceof RedactableError) {
|
||||
return new Error(e.fullMessage);
|
||||
}
|
||||
|
||||
return e instanceof Error ? e : new Error(String(e));
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ export function getPath(
|
||||
for (const codeFlows of result.codeFlows) {
|
||||
for (const threadFlow of codeFlows.threadFlows) {
|
||||
++index;
|
||||
if (index == key.pathIndex) return threadFlow;
|
||||
if (index === key.pathIndex) return threadFlow;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
|
||||
@@ -192,8 +192,8 @@ export function shouldHighlightLine(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (highlightedRegion.endLine == undefined) {
|
||||
return lineNumber == highlightedRegion.startLine;
|
||||
if (highlightedRegion.endLine === undefined) {
|
||||
return lineNumber === highlightedRegion.startLine;
|
||||
}
|
||||
|
||||
return lineNumber <= highlightedRegion.endLine;
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
import { QueryHistoryConfig } from "../config";
|
||||
import {
|
||||
showAndLogErrorMessage,
|
||||
showAndLogExceptionWithTelemetry,
|
||||
showAndLogInformationMessage,
|
||||
showAndLogWarningMessage,
|
||||
showBinaryChoiceDialog,
|
||||
@@ -27,6 +28,7 @@ import { DisposableObject } from "../pure/disposable-object";
|
||||
import { commandRunner } from "../commandRunner";
|
||||
import { ONE_HOUR_IN_MS, TWO_HOURS_IN_MS } from "../pure/time";
|
||||
import {
|
||||
asError,
|
||||
assertNever,
|
||||
getErrorMessage,
|
||||
getErrorStack,
|
||||
@@ -66,6 +68,7 @@ import { VariantAnalysisHistoryItem } from "./variant-analysis-history-item";
|
||||
import { getTotalResultCount } from "../remote-queries/shared/variant-analysis";
|
||||
import { App } from "../common/app";
|
||||
import { HistoryTreeDataProvider } from "./history-tree-data-provider";
|
||||
import { redactableError } from "../pure/errors";
|
||||
|
||||
/**
|
||||
* query-history-manager.ts
|
||||
@@ -621,7 +624,7 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
await Promise.all(
|
||||
this.treeDataProvider.allHistory.map(async (item) => {
|
||||
if (
|
||||
item.t == "local" &&
|
||||
item.t === "local" &&
|
||||
item.completedQuery &&
|
||||
!(await pathExists(item.completedQuery?.query.querySaveDir))
|
||||
) {
|
||||
@@ -809,7 +812,11 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
void showAndLogErrorMessage(getErrorMessage(e));
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError(
|
||||
asError(e),
|
||||
)`Failed to compare queries: ${getErrorMessage(e)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -834,7 +841,7 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
if (
|
||||
prevItemClick !== undefined &&
|
||||
now.valueOf() - prevItemClick.time.valueOf() < DOUBLE_CLICK_TIME &&
|
||||
finalSingleItem == prevItemClick.item
|
||||
finalSingleItem === prevItemClick.item
|
||||
) {
|
||||
// show original query file on double click
|
||||
await this.handleOpenQuery(finalSingleItem, [finalSingleItem]);
|
||||
@@ -1043,7 +1050,7 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
}
|
||||
|
||||
// If the JSON summary file location wasn't saved, display error
|
||||
if (finalSingleItem.jsonEvalLogSummaryLocation == undefined) {
|
||||
if (finalSingleItem.jsonEvalLogSummaryLocation === undefined) {
|
||||
this.warnInProgressEvalLogViewer();
|
||||
return;
|
||||
}
|
||||
@@ -1342,7 +1349,7 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
private updateTreeViewSelectionIfVisible() {
|
||||
if (this.treeView.visible) {
|
||||
const current = this.treeDataProvider.getCurrent();
|
||||
if (current != undefined) {
|
||||
if (current !== undefined) {
|
||||
// We must fire the onDidChangeTreeData event to ensure the current element can be selected
|
||||
// using `reveal` if the tree view was not visible when the current element was added.
|
||||
this.treeDataProvider.refresh();
|
||||
@@ -1374,13 +1381,20 @@ the file in the file explorer and dragging it into the workspace.`,
|
||||
try {
|
||||
await commands.executeCommand("revealFileInOS", uri);
|
||||
} catch (e) {
|
||||
void showAndLogErrorMessage(getErrorMessage(e));
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError(
|
||||
asError(e),
|
||||
)`Failed to reveal file in OS: ${getErrorMessage(e)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
void showAndLogErrorMessage(`Could not open file ${fileLocation}`);
|
||||
void extLogger.log(getErrorMessage(e));
|
||||
void extLogger.log(getErrorStack(e));
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError(asError(e))`Could not open file ${fileLocation}`,
|
||||
{
|
||||
fullMessage: `${getErrorMessage(e)}\n${getErrorStack(e)}`,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { pathExists, readFile, remove, mkdir, writeFile } from "fs-extra";
|
||||
import { dirname } from "path";
|
||||
|
||||
import { showAndLogErrorMessage } from "./helpers";
|
||||
import { showAndLogExceptionWithTelemetry } from "./helpers";
|
||||
import {
|
||||
asError,
|
||||
asyncFilter,
|
||||
getErrorMessage,
|
||||
getErrorStack,
|
||||
@@ -12,6 +13,7 @@ import { QueryHistoryInfo } from "./query-history/query-history-info";
|
||||
import { QueryStatus } from "./query-status";
|
||||
import { QueryEvaluationInfo } from "./run-queries-shared";
|
||||
import { QueryResultType } from "./pure/legacy-messages";
|
||||
import { redactableError } from "./pure/errors";
|
||||
|
||||
export async function deserializeQueryHistory(
|
||||
fsPath: string,
|
||||
@@ -24,8 +26,8 @@ export async function deserializeQueryHistory(
|
||||
const data = await readFile(fsPath, "utf8");
|
||||
const obj = JSON.parse(data);
|
||||
if (![1, 2].includes(obj.version)) {
|
||||
void showAndLogErrorMessage(
|
||||
`Can't parse query history. Unsupported query history format: v${obj.version}. `,
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError`Can't parse query history. Unsupported query history format: v${obj.version}.`,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
@@ -92,11 +94,12 @@ export async function deserializeQueryHistory(
|
||||
return !!resultsPath && (await pathExists(resultsPath));
|
||||
});
|
||||
} catch (e) {
|
||||
void showAndLogErrorMessage("Error loading query history.", {
|
||||
fullMessage: ["Error loading query history.", getErrorStack(e)].join(
|
||||
"\n",
|
||||
),
|
||||
});
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError(asError(e))`Error loading query history.`,
|
||||
{
|
||||
fullMessage: `Error loading query history.\n${getErrorStack(e)}`,
|
||||
},
|
||||
);
|
||||
// since the query history is invalid, it should be deleted so this error does not happen on next startup.
|
||||
await remove(fsPath);
|
||||
return [];
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ProgressCallback } from "../commandRunner";
|
||||
import { DatabaseItem } from "../databases";
|
||||
import {
|
||||
getOnDiskWorkspaceFolders,
|
||||
showAndLogErrorMessage,
|
||||
showAndLogExceptionWithTelemetry,
|
||||
showAndLogWarningMessage,
|
||||
tryGetQueryMetadata,
|
||||
} from "../helpers";
|
||||
@@ -15,6 +15,7 @@ import { QueryResultType } from "../pure/legacy-messages";
|
||||
import { InitialQueryInfo, LocalQueryInfo } from "../query-results";
|
||||
import { QueryEvaluationInfo, QueryWithResults } from "../run-queries-shared";
|
||||
import * as qsClient from "./queryserver-client";
|
||||
import { redactableError } from "../pure/errors";
|
||||
|
||||
/**
|
||||
* run-queries.ts
|
||||
@@ -109,9 +110,13 @@ export async function compileAndRunQueryAgainstDatabase(
|
||||
}
|
||||
|
||||
if (result.resultType !== messages.QueryResultType.SUCCESS) {
|
||||
const message = result.message || "Failed to run query";
|
||||
void extLogger.log(message);
|
||||
void showAndLogErrorMessage(message);
|
||||
const message = result?.message
|
||||
? redactableError`${result.message}`
|
||||
: redactableError`Failed to run query`;
|
||||
void extLogger.log(message.fullMessage);
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError`Failed to run query: ${message}`,
|
||||
);
|
||||
}
|
||||
let message;
|
||||
switch (result.resultType) {
|
||||
|
||||
@@ -149,7 +149,7 @@ export async function displayQuickQuery(
|
||||
} catch (e) {
|
||||
if (
|
||||
e instanceof ResponseError &&
|
||||
e.code == LSPErrorCodes.RequestCancelled
|
||||
e.code === LSPErrorCodes.RequestCancelled
|
||||
) {
|
||||
throw new UserCancellationException(getErrorMessage(e));
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { join } from "path";
|
||||
import { pathExists, readFile, writeFile } from "fs-extra";
|
||||
import {
|
||||
showAndLogErrorMessage,
|
||||
showAndLogExceptionWithTelemetry,
|
||||
showAndLogWarningMessage,
|
||||
tmpDir,
|
||||
} from "../../helpers";
|
||||
@@ -15,9 +15,10 @@ import {
|
||||
RemoteQueryResultIndex,
|
||||
RemoteQuerySuccessIndexItem,
|
||||
} from "../remote-query-result-index";
|
||||
import { getErrorMessage } from "../../pure/helpers-pure";
|
||||
import { asError, getErrorMessage } from "../../pure/helpers-pure";
|
||||
import { unzipFile } from "../../pure/zip";
|
||||
import { VariantAnalysis } from "../shared/variant-analysis";
|
||||
import { redactableError } from "../../pure/errors";
|
||||
|
||||
export const RESULT_INDEX_ARTIFACT_NAME = "result-index";
|
||||
|
||||
@@ -492,8 +493,10 @@ export async function getRepositoriesMetadata(
|
||||
}
|
||||
} while (cursor);
|
||||
} catch (e) {
|
||||
void showAndLogErrorMessage(
|
||||
`Error retrieving repository metadata for variant analysis: ${getErrorMessage(
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError(
|
||||
asError(e),
|
||||
)`Error retrieving repository metadata for variant analysis: ${getErrorMessage(
|
||||
e,
|
||||
)}`,
|
||||
);
|
||||
|
||||
@@ -6,10 +6,12 @@ import { RemoteQueriesResponse } from "./gh-api/remote-queries";
|
||||
import { submitRemoteQueries } from "./gh-api/gh-api-client";
|
||||
import {
|
||||
showAndLogErrorMessage,
|
||||
showAndLogExceptionWithTelemetry,
|
||||
showAndLogInformationMessage,
|
||||
} from "../helpers";
|
||||
import { getErrorMessage } from "../pure/helpers-pure";
|
||||
import { asError, getErrorMessage } from "../pure/helpers-pure";
|
||||
import { pluralize } from "../pure/word";
|
||||
import { redactableError } from "../pure/errors";
|
||||
|
||||
export async function runRemoteQueriesApiRequest(
|
||||
credentials: Credentials,
|
||||
@@ -43,7 +45,11 @@ export async function runRemoteQueriesApiRequest(
|
||||
`Controller repository was not found. Please make sure it's a valid repo name.${eol}`,
|
||||
);
|
||||
} else {
|
||||
void showAndLogErrorMessage(getErrorMessage(error));
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError(
|
||||
asError(error),
|
||||
)`Error submitting remote queries request: ${getErrorMessage(error)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import { ProgressCallback } from "../commandRunner";
|
||||
import {
|
||||
createTimestampFile,
|
||||
showAndLogErrorMessage,
|
||||
showAndLogExceptionWithTelemetry,
|
||||
showAndLogInformationMessage,
|
||||
showInformationMessageWithAction,
|
||||
} from "../helpers";
|
||||
@@ -36,12 +37,13 @@ import {
|
||||
} from "./remote-query-result";
|
||||
import { DownloadLink } from "./download-link";
|
||||
import { AnalysesResultsManager } from "./analyses-results-manager";
|
||||
import { assertNever, getErrorMessage } from "../pure/helpers-pure";
|
||||
import { asError, assertNever, getErrorMessage } from "../pure/helpers-pure";
|
||||
import { QueryStatus } from "../query-status";
|
||||
import { DisposableObject } from "../pure/disposable-object";
|
||||
import { AnalysisResults } from "./shared/analysis-result";
|
||||
import { runRemoteQueriesApiRequest } from "./remote-queries-api";
|
||||
import { App } from "../common/app";
|
||||
import { redactableError } from "../pure/errors";
|
||||
|
||||
const autoDownloadMaxSize = 300 * 1024;
|
||||
const autoDownloadMaxCount = 100;
|
||||
@@ -149,10 +151,19 @@ export class RemoteQueriesManager extends DisposableObject {
|
||||
// Open results in the background
|
||||
void this.openResults(remoteQuery, remoteQueryResult).then(
|
||||
noop,
|
||||
(err: unknown) => void showAndLogErrorMessage(getErrorMessage(err)),
|
||||
(e: unknown) =>
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError(
|
||||
asError(e),
|
||||
)`Could not open query results. ${getErrorMessage(e)}`,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
void showAndLogErrorMessage(`Could not open query results. ${e}`);
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError(
|
||||
asError(e),
|
||||
)`Could not open query results. ${getErrorMessage(e)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,8 +280,8 @@ export class RemoteQueriesManager extends DisposableObject {
|
||||
void showAndLogInformationMessage("Variant analysis was cancelled");
|
||||
} else if (queryWorkflowResult.status === "InProgress") {
|
||||
// Should not get here. Only including this to ensure `assertNever` uses proper type checking.
|
||||
void showAndLogErrorMessage(
|
||||
`Unexpected status: ${queryWorkflowResult.status}`,
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError`Unexpected status: ${queryWorkflowResult.status}`,
|
||||
);
|
||||
} else {
|
||||
// Ensure all cases are covered
|
||||
@@ -475,15 +486,18 @@ export class RemoteQueriesManager extends DisposableObject {
|
||||
// Ask if the user wants to open the results in the background.
|
||||
void this.askToOpenResults(remoteQuery, queryResult).then(
|
||||
noop,
|
||||
(err: unknown) => {
|
||||
void showAndLogErrorMessage(getErrorMessage(err));
|
||||
},
|
||||
(e: unknown) =>
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError(
|
||||
asError(e),
|
||||
)`Could not open query results. ${getErrorMessage(e)}`,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
const controllerRepo = `${remoteQuery.controllerRepository.owner}/${remoteQuery.controllerRepository.name}`;
|
||||
const workflowRunUrl = `https://github.com/${controllerRepo}/actions/runs/${remoteQuery.actionsWorkflowRunId}`;
|
||||
void showAndLogErrorMessage(
|
||||
`There was an issue retrieving the result for the query [${remoteQuery.queryName}](${workflowRunUrl}).`,
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError`There was an issue retrieving the result for the query [${remoteQuery.queryName}](${workflowRunUrl}).`,
|
||||
);
|
||||
this.remoteQueryStatusUpdateEventEmitter.fire({
|
||||
queryId,
|
||||
|
||||
@@ -153,7 +153,7 @@ export function tryGetRule(
|
||||
}
|
||||
|
||||
const ruleIndex = resultRule.index;
|
||||
if (ruleIndex != undefined) {
|
||||
if (ruleIndex !== undefined) {
|
||||
const toolComponentIndex = result.rule?.toolComponent?.index;
|
||||
const toolExtensions = sarifRun.tool.extensions;
|
||||
if (toolComponentIndex !== undefined && toolExtensions !== undefined) {
|
||||
|
||||
@@ -44,7 +44,7 @@ import {
|
||||
import PQueue from "p-queue";
|
||||
import {
|
||||
createTimestampFile,
|
||||
showAndLogErrorMessage,
|
||||
showAndLogExceptionWithTelemetry,
|
||||
showAndLogInformationMessage,
|
||||
showAndLogWarningMessage,
|
||||
} from "../helpers";
|
||||
@@ -62,6 +62,7 @@ import { URLSearchParams } from "url";
|
||||
import { DbManager } from "../databases/db-manager";
|
||||
import { isVariantAnalysisReposPanelEnabled } from "../config";
|
||||
import { App } from "../common/app";
|
||||
import { redactableError } from "../pure/errors";
|
||||
|
||||
export class VariantAnalysisManager
|
||||
extends DisposableObject
|
||||
@@ -262,8 +263,8 @@ export class VariantAnalysisManager
|
||||
|
||||
public async showView(variantAnalysisId: number): Promise<void> {
|
||||
if (!this.variantAnalyses.get(variantAnalysisId)) {
|
||||
void showAndLogErrorMessage(
|
||||
`No variant analysis found with id: ${variantAnalysisId}.`,
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError`No variant analysis found with id: ${variantAnalysisId}.`,
|
||||
);
|
||||
}
|
||||
if (!this.views.has(variantAnalysisId)) {
|
||||
|
||||
@@ -517,7 +517,7 @@ export async function determineSelectedQuery(
|
||||
let quickEvalPosition: messages.Position | undefined = undefined;
|
||||
let quickEvalText: string | undefined = undefined;
|
||||
if (quickEval) {
|
||||
if (editor == undefined) {
|
||||
if (editor === undefined) {
|
||||
throw new Error("Can't run quick evaluation without an active editor.");
|
||||
}
|
||||
if (editor.document.fileName !== queryPath) {
|
||||
|
||||
@@ -19,6 +19,7 @@ import * as appInsights from "applicationinsights";
|
||||
import { extLogger } from "./common";
|
||||
import { UserCancellationException } from "./commandRunner";
|
||||
import { showBinaryChoiceWithUrlDialog } from "./helpers";
|
||||
import { RedactableError } from "./pure/errors";
|
||||
|
||||
// Key is injected at build time through the APP_INSIGHTS_KEY environment variable.
|
||||
const key = "REPLACE-APP-INSIGHTS-KEY";
|
||||
@@ -187,6 +188,30 @@ export class TelemetryListener extends ConfigListener {
|
||||
);
|
||||
}
|
||||
|
||||
sendError(
|
||||
error: RedactableError,
|
||||
extraProperties?: { [key: string]: string },
|
||||
) {
|
||||
if (!this.reporter) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newTelemetryEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const properties: { [key: string]: string } = {
|
||||
isCanary: isCanary().toString(),
|
||||
message: error.redactedMessage,
|
||||
...extraProperties,
|
||||
};
|
||||
if (error.stack && error.stack !== "") {
|
||||
properties.stack = error.stack;
|
||||
}
|
||||
|
||||
this.reporter.sendTelemetryEvent("error", properties, {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a popup asking the user if they want to enable telemetry
|
||||
* for this extension.
|
||||
|
||||
@@ -30,11 +30,13 @@ import { DisposableObject } from "./pure/disposable-object";
|
||||
import { CodeQLCliServer } from "./cli";
|
||||
import {
|
||||
getOnDiskWorkspaceFolders,
|
||||
showAndLogErrorMessage,
|
||||
showAndLogExceptionWithTelemetry,
|
||||
showAndLogWarningMessage,
|
||||
} from "./helpers";
|
||||
import { testLogger } from "./common";
|
||||
import { DatabaseItem, DatabaseManager } from "./databases";
|
||||
import { asError, getErrorMessage } from "./pure/helpers-pure";
|
||||
import { redactableError } from "./pure/errors";
|
||||
|
||||
/**
|
||||
* Get the full path of the `.expected` file for the specified QL test.
|
||||
@@ -278,8 +280,10 @@ export class QLTestAdapter extends DisposableObject implements TestAdapter {
|
||||
// This method is invoked from Test Explorer UI, and testing indicates that Test
|
||||
// Explorer UI swallows any thrown exception without reporting it to the user.
|
||||
// So we need to display the error message ourselves and then rethrow.
|
||||
void showAndLogErrorMessage(
|
||||
`Cannot remove database ${database.name}: ${e}`,
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
redactableError(asError(e))`Cannot remove database ${
|
||||
database.name
|
||||
}: ${getErrorMessage(e)}`,
|
||||
);
|
||||
throw e;
|
||||
}
|
||||
@@ -306,7 +310,7 @@ export class QLTestAdapter extends DisposableObject implements TestAdapter {
|
||||
reopenedDatabase,
|
||||
closedDatabase.name,
|
||||
);
|
||||
if (currentDatabaseUri == uri) {
|
||||
if (currentDatabaseUri?.toString() === uri.toString()) {
|
||||
await this.databaseManager.setCurrentDatabaseItem(
|
||||
reopenedDatabase,
|
||||
true,
|
||||
|
||||
@@ -53,7 +53,7 @@ export const CodeSnippetLine = ({
|
||||
message &&
|
||||
severity &&
|
||||
highlightedRegion &&
|
||||
highlightedRegion.endLine == startingLineIndex + lineIndex;
|
||||
highlightedRegion.endLine === startingLineIndex + lineIndex;
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
@@ -177,7 +177,7 @@ export class PathTable extends React.Component<PathTableProps, PathTableState> {
|
||||
msg: string | undefined,
|
||||
locationHint: string,
|
||||
): JSX.Element | undefined {
|
||||
if (msg == undefined) return undefined;
|
||||
if (msg === undefined) return undefined;
|
||||
return <span title={locationHint}>{msg}</span>;
|
||||
}
|
||||
|
||||
@@ -304,7 +304,7 @@ export class PathTable extends React.Component<PathTableProps, PathTableState> {
|
||||
const paths: Sarif.ThreadFlow[] = Keys.getAllPaths(result);
|
||||
|
||||
const indices =
|
||||
paths.length == 1
|
||||
paths.length === 1
|
||||
? [resultKey, { ...resultKey, pathIndex: 0 }]
|
||||
: /* if there's exactly one path, auto-expand
|
||||
* the path when expanding the result */
|
||||
|
||||
@@ -80,7 +80,7 @@ export class Graph extends React.Component<GraphProps> {
|
||||
graphviz(`#${graphId}`)
|
||||
.options(options)
|
||||
.attributer(function (d) {
|
||||
if (d.tag == "a") {
|
||||
if (d.tag === "a") {
|
||||
const url = d.attributes["xlink:href"] || d.attributes["href"];
|
||||
const loc = tryGetLocationFromString(url);
|
||||
if (loc !== undefined) {
|
||||
@@ -94,13 +94,13 @@ export class Graph extends React.Component<GraphProps> {
|
||||
}
|
||||
|
||||
if ("fill" in d.attributes) {
|
||||
d.attributes.fill = d.tag == "text" ? color : backgroundColor;
|
||||
d.attributes.fill = d.tag === "text" ? color : backgroundColor;
|
||||
}
|
||||
if ("stroke" in d.attributes) {
|
||||
// There is no proper way to identify the element containing the graph (which we
|
||||
// don't want a border around), as it is just has tag 'polygon'. Instead we assume
|
||||
// that the first polygon we see is that element
|
||||
if (d.tag != "polygon" || !firstPolygon) {
|
||||
if (d.tag !== "polygon" || !firstPolygon) {
|
||||
d.attributes.stroke = borderColor;
|
||||
} else {
|
||||
firstPolygon = false;
|
||||
|
||||
@@ -92,7 +92,7 @@ export class ResultTables extends React.Component<
|
||||
// @ts-ignore 2783
|
||||
this.props.rawResultSets.map((rs) => ({ t: "RawResultSet", ...rs }));
|
||||
|
||||
if (this.props.interpretation != undefined) {
|
||||
if (this.props.interpretation !== undefined) {
|
||||
const tableName = this.getInterpretedTableName();
|
||||
resultSets.push({
|
||||
t: "InterpretedResultSet",
|
||||
@@ -307,10 +307,11 @@ export class ResultTables extends React.Component<
|
||||
const resultSetNames = this.getResultSetNames();
|
||||
|
||||
const resultSet = resultSets.find(
|
||||
(resultSet) => resultSet.schema.name == selectedTable,
|
||||
(resultSet) => resultSet.schema.name === selectedTable,
|
||||
);
|
||||
const nonemptyRawResults = resultSets.some(
|
||||
(resultSet) => resultSet.t == "RawResultSet" && resultSet.rows.length > 0,
|
||||
(resultSet) =>
|
||||
resultSet.t === "RawResultSet" && resultSet.rows.length > 0,
|
||||
);
|
||||
const numberOfResults = resultSet && renderResultCountString(resultSet);
|
||||
|
||||
|
||||
@@ -289,7 +289,7 @@ export class ResultsApp extends React.Component<
|
||||
}
|
||||
sortStates={displayedResults.results.sortStates}
|
||||
interpretedSortState={
|
||||
data?.t == "SarifInterpretationData" ? data.sortState : undefined
|
||||
data?.t === "SarifInterpretationData" ? data.sortState : undefined
|
||||
}
|
||||
isLoadingNewResults={
|
||||
this.state.isExpectingResultsUpdate ||
|
||||
|
||||
@@ -130,7 +130,7 @@ const canSelect = (
|
||||
status: VariantAnalysisRepoStatus | undefined,
|
||||
downloadStatus: VariantAnalysisScannedRepositoryDownloadStatus | undefined,
|
||||
) =>
|
||||
status == VariantAnalysisRepoStatus.Succeeded &&
|
||||
status === VariantAnalysisRepoStatus.Succeeded &&
|
||||
downloadStatus === VariantAnalysisScannedRepositoryDownloadStatus.Succeeded;
|
||||
|
||||
const isExpandableContentLoaded = (
|
||||
|
||||
@@ -27,13 +27,15 @@ export const VariantAnalysisStatusStats = ({
|
||||
completedAt,
|
||||
onViewLogsClick,
|
||||
}: VariantAnalysisStatusStatsProps) => {
|
||||
if (variantAnalysisStatus === VariantAnalysisStatus.InProgress) {
|
||||
return <Icon className="codicon codicon-loading codicon-modifier-spin" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<span>{completedAt !== undefined ? formatDate(completedAt) : "-"}</span>
|
||||
{variantAnalysisStatus === VariantAnalysisStatus.InProgress ? (
|
||||
<div>
|
||||
<Icon className="codicon codicon-loading codicon-modifier-spin" />
|
||||
</div>
|
||||
) : (
|
||||
<span>{completedAt !== undefined ? formatDate(completedAt) : "-"}</span>
|
||||
)}
|
||||
{onViewLogsClick && (
|
||||
<VSCodeLink onClick={onViewLogsClick}>View logs</VSCodeLink>
|
||||
)}
|
||||
|
||||
41
extensions/ql-vscode/test/unit-tests/pure/errors.test.ts
Normal file
41
extensions/ql-vscode/test/unit-tests/pure/errors.test.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { redactableError, RedactableError } from "../../../src/pure/errors";
|
||||
|
||||
describe("errorMessage", () => {
|
||||
it("creates a RedactableError", () => {
|
||||
expect(redactableError`Failed to create database ${"foo"}`).toBeInstanceOf(
|
||||
RedactableError,
|
||||
);
|
||||
});
|
||||
|
||||
it("toString() matches the given message", () => {
|
||||
expect(
|
||||
redactableError`Failed to create database ${"foo"}`.toString(),
|
||||
).toEqual("Failed to create database foo");
|
||||
});
|
||||
|
||||
it("fullMessage matches the given message", () => {
|
||||
expect(
|
||||
redactableError`Failed to create database ${"foo"}`.fullMessage,
|
||||
).toEqual("Failed to create database foo");
|
||||
});
|
||||
|
||||
it("redactedMessage redacts the given message", () => {
|
||||
expect(
|
||||
redactableError`Failed to create database ${"foo"}`.redactedMessage,
|
||||
).toEqual("Failed to create database [REDACTED]");
|
||||
});
|
||||
|
||||
it("fullMessage returns the correct message for nested redactableError", () => {
|
||||
expect(
|
||||
redactableError`Failed to create database ${redactableError`foo ${"bar"}`}`
|
||||
.fullMessage,
|
||||
).toEqual("Failed to create database foo bar");
|
||||
});
|
||||
|
||||
it("redactedMessage returns the correct message for nested redactableError", () => {
|
||||
expect(
|
||||
redactableError`Failed to create database ${redactableError`foo ${"bar"}`}`
|
||||
.redactedMessage,
|
||||
).toEqual("Failed to create database foo [REDACTED]");
|
||||
});
|
||||
});
|
||||
@@ -19,8 +19,8 @@ describe("Packaging commands", () => {
|
||||
const progress = jest.fn();
|
||||
let quickPickSpy: jest.SpiedFunction<typeof window.showQuickPick>;
|
||||
let inputBoxSpy: jest.SpiedFunction<typeof window.showInputBox>;
|
||||
let showAndLogErrorMessageSpy: jest.SpiedFunction<
|
||||
typeof helpers.showAndLogErrorMessage
|
||||
let showAndLogExceptionWithTelemetrySpy: jest.SpiedFunction<
|
||||
typeof helpers.showAndLogExceptionWithTelemetry
|
||||
>;
|
||||
let showAndLogInformationMessageSpy: jest.SpiedFunction<
|
||||
typeof helpers.showAndLogInformationMessage
|
||||
@@ -33,8 +33,8 @@ describe("Packaging commands", () => {
|
||||
inputBoxSpy = jest
|
||||
.spyOn(window, "showInputBox")
|
||||
.mockResolvedValue(undefined);
|
||||
showAndLogErrorMessageSpy = jest
|
||||
.spyOn(helpers, "showAndLogErrorMessage")
|
||||
showAndLogExceptionWithTelemetrySpy = jest
|
||||
.spyOn(helpers, "showAndLogExceptionWithTelemetry")
|
||||
.mockResolvedValue(undefined);
|
||||
showAndLogInformationMessageSpy = jest
|
||||
.spyOn(helpers, "showAndLogInformationMessage")
|
||||
@@ -85,9 +85,10 @@ describe("Packaging commands", () => {
|
||||
|
||||
await handleDownloadPacks(cli, progress);
|
||||
|
||||
expect(showAndLogErrorMessageSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Unable to download all packs."),
|
||||
);
|
||||
expect(showAndLogExceptionWithTelemetrySpy).toHaveBeenCalled();
|
||||
expect(
|
||||
showAndLogExceptionWithTelemetrySpy.mock.calls[0][0].fullMessage,
|
||||
).toEqual("Unable to download all packs. See log for more details.");
|
||||
});
|
||||
|
||||
it("should install valid workspace pack", async () => {
|
||||
|
||||
@@ -81,7 +81,7 @@ describe("queryResolver", () => {
|
||||
expect(true).toBe(false);
|
||||
} catch (e) {
|
||||
expect(getErrorMessage(e)).toBe(
|
||||
"Couldn't find any queries tagged ide-contextual-queries/local-definitions in any of the following packs: my-qlpack.",
|
||||
'No definitions queries (tagged "ide-contextual-queries/local-definitions") could be found in the current library path (tried searching the following packs: my-qlpack). Try upgrading the CodeQL libraries. If that doesn\'t work, then definitions queries are not yet available for this language.',
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -125,7 +125,7 @@ describe("serialize and deserialize", () => {
|
||||
}
|
||||
});
|
||||
expectedHistory.forEach((info) => {
|
||||
if (info.t == "local" && info.completedQuery) {
|
||||
if (info.t === "local" && info.completedQuery) {
|
||||
(info.completedQuery as any).dispose = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -13,6 +13,7 @@ import { UserCancellationException } from "../../../src/commandRunner";
|
||||
import { ENABLE_TELEMETRY } from "../../../src/config";
|
||||
import * as Config from "../../../src/config";
|
||||
import { createMockExtensionContext } from "./index";
|
||||
import { redactableError } from "../../../src/pure/errors";
|
||||
|
||||
// setting preferences can trigger lots of background activity
|
||||
// so need to bump up the timeout of this test.
|
||||
@@ -390,13 +391,21 @@ describe("telemetry reporting", () => {
|
||||
});
|
||||
|
||||
describe("when new telementry is not enabled", () => {
|
||||
it("should not send a telementry event", async () => {
|
||||
it("should not send a ui-interaction telementry event", async () => {
|
||||
await telemetryListener.initialize();
|
||||
|
||||
telemetryListener.sendUIInteraction("test");
|
||||
|
||||
expect(sendTelemetryEventSpy).not.toBeCalled();
|
||||
});
|
||||
|
||||
it("should not send an error telementry event", async () => {
|
||||
await telemetryListener.initialize();
|
||||
|
||||
telemetryListener.sendError(redactableError`test`);
|
||||
|
||||
expect(sendTelemetryEventSpy).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when new telementry is enabled", () => {
|
||||
@@ -404,7 +413,7 @@ describe("telemetry reporting", () => {
|
||||
jest.spyOn(Config, "newTelemetryEnabled").mockReturnValue(true);
|
||||
});
|
||||
|
||||
it("should not send a telementry event", async () => {
|
||||
it("should send a ui-interaction telementry event", async () => {
|
||||
await telemetryListener.initialize();
|
||||
|
||||
telemetryListener.sendUIInteraction("test");
|
||||
@@ -418,6 +427,42 @@ describe("telemetry reporting", () => {
|
||||
{},
|
||||
);
|
||||
});
|
||||
|
||||
it("should send an error telementry event", async () => {
|
||||
await telemetryListener.initialize();
|
||||
|
||||
telemetryListener.sendError(redactableError`test`);
|
||||
|
||||
expect(sendTelemetryEventSpy).toHaveBeenCalledWith(
|
||||
"error",
|
||||
{
|
||||
message: "test",
|
||||
isCanary,
|
||||
stack: expect.any(String),
|
||||
},
|
||||
{},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("should redact error message contents", async () => {
|
||||
jest.spyOn(Config, "newTelemetryEnabled").mockReturnValue(true);
|
||||
await telemetryListener.initialize();
|
||||
|
||||
telemetryListener.sendError(
|
||||
redactableError`test message with secret information: ${42} and more ${"secret"} parts`,
|
||||
);
|
||||
|
||||
expect(sendTelemetryEventSpy).toHaveBeenCalledWith(
|
||||
"error",
|
||||
{
|
||||
message:
|
||||
"test message with secret information: [REDACTED] and more [REDACTED] parts",
|
||||
isCanary,
|
||||
stack: expect.any(String),
|
||||
},
|
||||
{},
|
||||
);
|
||||
});
|
||||
|
||||
async function enableTelemetry(section: string, value: boolean | undefined) {
|
||||
|
||||
Reference in New Issue
Block a user