Convert RedactableError to an Error class of its own

This commit is contained in:
Robert
2023-01-27 17:36:56 +00:00
parent 160a6cca76
commit 72c8e0bcb6
24 changed files with 174 additions and 193 deletions

View File

@@ -27,7 +27,7 @@ import { commandRunner } from "./commandRunner";
import { DisposableObject } from "./pure/disposable-object";
import { showAndLogExceptionWithTelemetry } from "./helpers";
import { asError, getErrorMessage } from "./pure/helpers-pure";
import { redactableErrorMessage } from "./pure/errors";
import { redactableError } from "./pure/errors";
export interface AstItem {
id: BqrsId;
@@ -150,10 +150,9 @@ export class AstViewer extends DisposableObject {
},
(error: unknown) =>
showAndLogExceptionWithTelemetry(
asError(error),
redactableErrorMessage`Failed to reveal AST: ${getErrorMessage(
error,
)}`,
redactableError(
asError(error),
)`Failed to reveal AST: ${getErrorMessage(error)}`,
),
);
}
@@ -214,10 +213,9 @@ export class AstViewer extends DisposableObject {
},
(error: unknown) =>
showAndLogExceptionWithTelemetry(
asError(error),
redactableErrorMessage`Failed to reveal AST: ${getErrorMessage(
error,
)}`,
redactableError(
asError(error),
)`Failed to reveal AST: ${getErrorMessage(error)}`,
),
);
}

View File

@@ -13,7 +13,7 @@ import {
import { extLogger } from "./common";
import { asError, getErrorMessage, getErrorStack } from "./pure/helpers-pure";
import { telemetryListener } from "./telemetry";
import { redactableErrorMessage } from "./pure/errors";
import { redactableError } from "./pure/errors";
export class UserCancellationException extends Error {
/**
@@ -129,10 +129,10 @@ export function commandRunner(
try {
return await task(...args);
} catch (e) {
const errorMessage = redactableErrorMessage`${
error = asError(e);
const errorMessage = redactableError(error)`${
getErrorMessage(e) || e
} (${commandId})`;
error = asError(e);
const errorStack = getErrorStack(e);
if (e instanceof UserCancellationException) {
// User has cancelled this action manually
@@ -146,7 +146,7 @@ export function commandRunner(
const fullMessage = errorStack
? `${errorMessage.fullMessage}\n${errorStack}`
: errorMessage.fullMessage;
void showAndLogExceptionWithTelemetry(error, errorMessage, {
void showAndLogExceptionWithTelemetry(errorMessage, {
fullMessage,
extraTelemetryProperties: {
command: commandId,
@@ -187,10 +187,10 @@ export function commandRunnerWithProgress<R>(
try {
return await withProgress(progressOptionsWithDefaults, task, ...args);
} catch (e) {
const errorMessage = redactableErrorMessage`${
error = asError(e);
const errorMessage = redactableError`${
getErrorMessage(e) || e
} (${commandId})`;
error = asError(e);
const errorStack = getErrorStack(e);
if (e instanceof UserCancellationException) {
// User has cancelled this action manually
@@ -206,7 +206,7 @@ export function commandRunnerWithProgress<R>(
const fullMessage = errorStack
? `${errorMessage.fullMessage}\n${errorStack}`
: errorMessage.fullMessage;
void showAndLogExceptionWithTelemetry(error, errorMessage, {
void showAndLogExceptionWithTelemetry(errorMessage, {
outputLogger,
fullMessage,
extraTelemetryProperties: {

View File

@@ -18,7 +18,7 @@ import { createInitialQueryInfo } from "../run-queries-shared";
import { CancellationToken, Uri } from "vscode";
import { ProgressCallback } from "../commandRunner";
import { QueryRunner } from "../queryRunner";
import { redactableErrorMessage } from "../pure/errors";
import { redactableError } from "../pure/errors";
export async function qlpackOfDatabase(
cli: CodeQLCliServer,
@@ -92,14 +92,13 @@ export async function resolveQueries(
const keyTypeName = nameOfKeyType(keyType);
const keyTypeTag = tagOfKeyType(keyType);
const joinedPacksToSearch = packsToSearch.join(", ");
const errorMessage = redactableErrorMessage`No ${keyTypeName} queries (tagged "${keyTypeTag}") could be found in the \
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.`;
const e = new Error(errorMessage.fullMessage);
void showAndLogExceptionWithTelemetry(e, errorMessage);
throw e;
void showAndLogExceptionWithTelemetry(error);
throw error;
}
async function resolveContextualQuery(

View File

@@ -42,7 +42,7 @@ import { QueryRunner } from "./queryRunner";
import { isCanary } from "./config";
import { App } from "./common/app";
import { Credentials } from "./common/authentication";
import { redactableErrorMessage } from "./pure/errors";
import { redactableError } from "./pure/errors";
enum SortOrder {
NameAsc = "NameAsc",
@@ -347,10 +347,9 @@ export class DatabaseUI extends DisposableObject {
await this.chooseAndSetDatabase(true, progress, token);
} catch (e) {
void showAndLogExceptionWithTelemetry(
asError(e),
redactableErrorMessage`Failed to choose and set database: ${getErrorMessage(
e,
)}`,
redactableError(
asError(e),
)`Failed to choose and set database: ${getErrorMessage(e)}`,
);
}
};
@@ -401,10 +400,9 @@ export class DatabaseUI extends DisposableObject {
await remove(dbDir);
} catch (e) {
void showAndLogExceptionWithTelemetry(
asError(e),
redactableErrorMessage`Failed to delete orphaned database: ${getErrorMessage(
e,
)}`,
redactableError(
asError(e),
)`Failed to delete orphaned database: ${getErrorMessage(e)}`,
);
failures.push(`${basename(dbDir)}`);
}
@@ -429,10 +427,9 @@ export class DatabaseUI extends DisposableObject {
await this.chooseAndSetDatabase(false, progress, token);
} catch (e: unknown) {
void showAndLogExceptionWithTelemetry(
asError(e),
redactableErrorMessage`Failed to choose and set database: ${getErrorMessage(
e,
)}`,
redactableError(
asError(e),
)`Failed to choose and set database: ${getErrorMessage(e)}`,
);
}
};

View File

@@ -22,7 +22,7 @@ import { Logger, extLogger } from "./common";
import { asError, getErrorMessage } from "./pure/helpers-pure";
import { QueryRunner } from "./queryRunner";
import { pathsEqual } from "./pure/files";
import { redactableErrorMessage } from "./pure/errors";
import { redactableError } from "./pure/errors";
/**
* databases.ts
@@ -796,10 +796,9 @@ export class DatabaseManager extends DisposableObject {
} catch (e) {
// database list had an unexpected type - nothing to be done?
void showAndLogExceptionWithTelemetry(
asError(e),
redactableErrorMessage`Database list loading failed: ${getErrorMessage(
e,
)}`,
redactableError(
asError(e),
)`Database list loading failed: ${getErrorMessage(e)}`,
);
}

View File

@@ -12,7 +12,7 @@ import { commandRunner } from "./commandRunner";
import { DisposableObject } from "./pure/disposable-object";
import { showAndLogExceptionWithTelemetry } from "./helpers";
import { asError, getErrorMessage } from "./pure/helpers-pure";
import { redactableErrorMessage } from "./pure/errors";
import { redactableError } from "./pure/errors";
export interface EvalLogTreeItem {
label?: string;
@@ -108,10 +108,9 @@ export class EvalLogViewer extends DisposableObject {
},
(err: unknown) =>
showAndLogExceptionWithTelemetry(
asError(err),
redactableErrorMessage`Failed to reveal tree view: ${getErrorMessage(
err,
)}`,
redactableError(
asError(err),
)`Failed to reveal tree view: ${getErrorMessage(err)}`,
),
);
}

View File

@@ -138,7 +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 { redactableErrorMessage } from "./pure/errors";
import { redactableError } from "./pure/errors";
/**
* extension.ts
@@ -717,8 +717,9 @@ async function activateWithInstalledDistribution(
await compareView.showResults(from, to);
} catch (e) {
void showAndLogExceptionWithTelemetry(
asError(e),
redactableErrorMessage`Failed to show results: ${getErrorMessage(e)}`,
redactableError(asError(e))`Failed to show results: ${getErrorMessage(
e,
)}`,
);
}
}
@@ -815,9 +816,9 @@ async function activateWithInstalledDistribution(
const errorMessage = getErrorMessage(e).includes(
"Generating qhelp in markdown",
)
? redactableErrorMessage`Could not generate markdown from ${pathToQhelp}: Bad formatting in .qhelp file.`
: redactableErrorMessage`Could not open a preview of the generated file (${absolutePathToMd}).`;
void showAndLogExceptionWithTelemetry(asError(e), errorMessage, {
? 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)}`,
});
}

View File

@@ -22,7 +22,7 @@ import { UserCancellationException } from "./commandRunner";
import { extLogger, OutputChannelLogger } from "./common";
import { QueryMetadata } from "./pure/interface-types";
import { telemetryListener } from "./telemetry";
import { RedactableErrorMessage } from "./pure/errors";
import { RedactableError } from "./pure/errors";
// Shared temporary folder for the extension.
export const tmpDir = dirSync({
@@ -63,27 +63,19 @@ interface ShowAndLogOptions {
}
/**
* Show an error message and log it to the console
* Show an error message, log it to the console, and emit redacted information as telemetry
*
* @param error The error message to show, either as a Error or string. Will not be included in the
* telemetry event, and therefore may safely include sensitive information.
* @param message A message to show to the user. Will also be included in the telemetry event,
* but only the redacated message will be sent.
* @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: Error,
message: RedactableErrorMessage,
error: RedactableError,
options: ShowAndLogExceptionOptions = {},
): Promise<string | undefined> {
telemetryListener?.sendError(
message,
error.stack,
options.extraTelemetryProperties,
);
return showAndLogErrorMessage(message.fullMessage, options);
telemetryListener?.sendError(error, options.extraTelemetryProperties);
return showAndLogErrorMessage(error.fullMessage, options);
}
/**

View File

@@ -67,7 +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 { redactableErrorMessage } from "./pure/errors";
import { redactableError } from "./pure/errors";
/**
* interface.ts
@@ -294,10 +294,9 @@ export class ResultsView extends AbstractWebview<
}
} catch (e) {
void showAndLogExceptionWithTelemetry(
asError(e),
redactableErrorMessage`Error handling message from results view: ${getErrorMessage(
e,
)}`,
redactableError(
asError(e),
)`Error handling message from results view: ${getErrorMessage(e)}`,
{
fullMessage: getErrorStack(e),
},
@@ -344,8 +343,7 @@ export class ResultsView extends AbstractWebview<
): Promise<void> {
if (this._displayedQuery === undefined) {
void showAndLogExceptionWithTelemetry(
asError("Failed to sort results since evaluation info was unknown."),
redactableErrorMessage`Failed to sort results since evaluation info was unknown.`,
redactableError`Failed to sort results since evaluation info was unknown.`,
);
return;
}
@@ -363,8 +361,7 @@ export class ResultsView extends AbstractWebview<
): Promise<void> {
if (this._displayedQuery === undefined) {
void showAndLogExceptionWithTelemetry(
asError("Failed to sort results since evaluation info was unknown."),
redactableErrorMessage`Failed to sort results since evaluation info was unknown.`,
redactableError`Failed to sort results since evaluation info was unknown.`,
);
return;
}
@@ -773,8 +770,9 @@ export class ResultsView extends AbstractWebview<
// If interpretation fails, accept the error and continue
// trying to render uninterpreted results anyway.
void showAndLogExceptionWithTelemetry(
asError(e),
redactableErrorMessage`Showing raw results instead of interpreted ones due to an error. ${getErrorMessage(
redactableError(
asError(e),
)`Showing raw results instead of interpreted ones due to an error. ${getErrorMessage(
e,
)}`,
);

View File

@@ -22,7 +22,7 @@ import * as qsClient from "./queryserver-client";
import { asError, getErrorMessage } from "../pure/helpers-pure";
import { compileDatabaseUpgradeSequence } from "./upgrades";
import { QueryEvaluationInfo, QueryWithResults } from "../run-queries-shared";
import { redactableErrorMessage } from "../pure/errors";
import { redactableError } from "../pure/errors";
/**
* A collection of evaluation-time information about a query,
@@ -369,8 +369,11 @@ export async function compileAndRunQueryAgainstDatabase(
void extLogger.log("Did not find any available ML models.");
}
} catch (e) {
const notificationMessage = redactableErrorMessage`Couldn't resolve available ML models for ${qlProgram.queryPath}. Running the query without any ML models: ${e}.`;
void showAndLogExceptionWithTelemetry(asError(e), notificationMessage);
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();
@@ -422,14 +425,11 @@ export async function compileAndRunQueryAgainstDatabase(
queryInfo,
);
if (result.resultType !== messages.QueryResultType.SUCCESS) {
const message = result.message
? redactableErrorMessage`${result.message}`
: redactableErrorMessage`Failed to run query`;
void extLogger.log(message.fullMessage);
void showAndLogExceptionWithTelemetry(
asError(message),
redactableErrorMessage`Failed to run query: ${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);

View File

@@ -12,7 +12,7 @@ import * as tmp from "tmp-promise";
import { dirname } from "path";
import { DatabaseItem } from "../databases";
import { asError, getErrorMessage } from "../pure/helpers-pure";
import { redactableErrorMessage } from "../pure/errors";
import { redactableError } from "../pure/errors";
/**
* Maximum number of lines to include from database upgrade message,
@@ -212,10 +212,9 @@ export async function upgradeDatabaseExplicit(
);
} catch (e) {
void showAndLogExceptionWithTelemetry(
asError(e),
redactableErrorMessage`Compilation of database upgrades failed: ${getErrorMessage(
e,
)}`,
redactableError(
asError(e),
)`Compilation of database upgrades failed: ${getErrorMessage(e)}`,
);
return;
} finally {
@@ -223,14 +222,11 @@ export async function upgradeDatabaseExplicit(
}
if (!compileUpgradeResult.compiledUpgrades) {
const error =
redactableErrorMessage`${compileUpgradeResult.error}` ||
redactableErrorMessage`[no error message available]`;
const error = compileUpgradeResult.error
? redactableError`${compileUpgradeResult.error}`
: redactableError`[no error message available]`;
void showAndLogExceptionWithTelemetry(
asError(
`Compilation of database upgrades failed: ${error.fullMessage}`,
),
redactableErrorMessage`Compilation of database upgrades failed: ${error}`,
redactableError`Compilation of database upgrades failed: ${error}`,
);
return;
}
@@ -263,8 +259,9 @@ export async function upgradeDatabaseExplicit(
return result;
} catch (e) {
void showAndLogExceptionWithTelemetry(
asError(e),
redactableErrorMessage`Database upgrade failed: ${getErrorMessage(e)}`,
redactableError(asError(e))`Database upgrade failed: ${getErrorMessage(
e,
)}`,
);
return;
} finally {

View File

@@ -8,7 +8,7 @@ import { QuickPickItem, window } from "vscode";
import { ProgressCallback, UserCancellationException } from "./commandRunner";
import { extLogger } from "./common";
import { asError, getErrorStack } from "./pure/helpers-pure";
import { redactableErrorMessage } from "./pure/errors";
import { redactableError } from "./pure/errors";
const QUERY_PACKS = [
"codeql/cpp-queries",
@@ -69,8 +69,9 @@ export async function handleDownloadPacks(
void showAndLogInformationMessage("Finished downloading packs.");
} catch (error) {
void showAndLogExceptionWithTelemetry(
asError(error),
redactableErrorMessage`Unable to download all packs. See log for more details.`,
redactableError(
asError(error),
)`Unable to download all packs. See log for more details.`,
{
fullMessage: getErrorStack(error),
},

View File

@@ -1,8 +1,16 @@
export class RedactableErrorMessage {
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;
@@ -22,7 +30,7 @@ export class RedactableErrorMessage {
private getValue(index: number): unknown {
const value = this.values[index];
if (value instanceof RedactableErrorMessage) {
if (value instanceof RedactableError) {
return value.fullMessage;
}
return value;
@@ -30,7 +38,7 @@ export class RedactableErrorMessage {
private getRedactedValue(index: number): unknown {
const value = this.values[index];
if (value instanceof RedactableErrorMessage) {
if (value instanceof RedactableError) {
return value.redactedMessage;
}
return "[REDACTED]";
@@ -41,9 +49,24 @@ export class RedactableErrorMessage {
}
}
export function redactableErrorMessage(
export function redactableError(
strings: TemplateStringsArray,
...values: unknown[]
): RedactableErrorMessage {
return new RedactableErrorMessage(strings, values);
): 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);
}
}

View File

@@ -5,7 +5,7 @@
* 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 { RedactableErrorMessage } from "./errors";
import { RedactableError } from "./errors";
/**
* This error is used to indicate a runtime failure of an exhaustivity check enforced at compile time.
@@ -49,7 +49,7 @@ 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): string {
if (e instanceof RedactableErrorMessage) {
if (e instanceof RedactableError) {
return e.fullMessage;
}
@@ -61,7 +61,7 @@ export function getErrorStack(e: unknown): string {
}
export function asError(e: unknown): Error {
if (e instanceof RedactableErrorMessage) {
if (e instanceof RedactableError) {
return new Error(e.fullMessage);
}

View File

@@ -68,7 +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 { redactableErrorMessage } from "../pure/errors";
import { redactableError } from "../pure/errors";
/**
* query-history-manager.ts
@@ -813,10 +813,9 @@ export class QueryHistoryManager extends DisposableObject {
}
} catch (e) {
void showAndLogExceptionWithTelemetry(
asError(e),
redactableErrorMessage`Failed to compare queries: ${getErrorMessage(
e,
)}`,
redactableError(
asError(e),
)`Failed to compare queries: ${getErrorMessage(e)}`,
);
}
}
@@ -1383,17 +1382,15 @@ the file in the file explorer and dragging it into the workspace.`,
await commands.executeCommand("revealFileInOS", uri);
} catch (e) {
void showAndLogExceptionWithTelemetry(
asError(e),
redactableErrorMessage`Failed to reveal file in OS: ${getErrorMessage(
e,
)}`,
redactableError(
asError(e),
)`Failed to reveal file in OS: ${getErrorMessage(e)}`,
);
}
}
} else {
void showAndLogExceptionWithTelemetry(
asError(e),
redactableErrorMessage`Could not open file ${fileLocation}`,
redactableError(asError(e))`Could not open file ${fileLocation}`,
{
fullMessage: `${getErrorMessage(e)}\n${getErrorStack(e)}`,
},

View File

@@ -13,7 +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 { redactableErrorMessage } from "./pure/errors";
import { redactableError } from "./pure/errors";
export async function deserializeQueryHistory(
fsPath: string,
@@ -27,10 +27,7 @@ export async function deserializeQueryHistory(
const obj = JSON.parse(data);
if (![1, 2].includes(obj.version)) {
void showAndLogExceptionWithTelemetry(
asError(
`Can't parse query history. Unsupported query history format: v${obj.version}.`,
),
redactableErrorMessage`Can't parse query history. Unsupported query history format: v${obj.version}.`,
redactableError`Can't parse query history. Unsupported query history format: v${obj.version}.`,
);
return [];
}
@@ -98,8 +95,7 @@ export async function deserializeQueryHistory(
});
} catch (e) {
void showAndLogExceptionWithTelemetry(
asError(e),
redactableErrorMessage`Error loading query history.`,
redactableError(asError(e))`Error loading query history.`,
{
fullMessage: `Error loading query history.\n${getErrorStack(e)}`,
},

View File

@@ -15,8 +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 { asError } from "../pure/helpers-pure";
import { redactableErrorMessage } from "../pure/errors";
import { redactableError } from "../pure/errors";
/**
* run-queries.ts
@@ -112,12 +111,11 @@ export async function compileAndRunQueryAgainstDatabase(
if (result.resultType !== messages.QueryResultType.SUCCESS) {
const message = result?.message
? redactableErrorMessage`${result.message}`
: redactableErrorMessage`Failed to run query`;
? redactableError`${result.message}`
: redactableError`Failed to run query`;
void extLogger.log(message.fullMessage);
void showAndLogExceptionWithTelemetry(
asError(message),
redactableErrorMessage`Failed to run query: ${message}`,
redactableError`Failed to run query: ${message}`,
);
}
let message;

View File

@@ -18,7 +18,7 @@ import {
import { asError, getErrorMessage } from "../../pure/helpers-pure";
import { unzipFile } from "../../pure/zip";
import { VariantAnalysis } from "../shared/variant-analysis";
import { redactableErrorMessage } from "../../pure/errors";
import { redactableError } from "../../pure/errors";
export const RESULT_INDEX_ARTIFACT_NAME = "result-index";
@@ -494,8 +494,9 @@ export async function getRepositoriesMetadata(
} while (cursor);
} catch (e) {
void showAndLogExceptionWithTelemetry(
asError(e),
redactableErrorMessage`Error retrieving repository metadata for variant analysis: ${getErrorMessage(
redactableError(
asError(e),
)`Error retrieving repository metadata for variant analysis: ${getErrorMessage(
e,
)}`,
);

View File

@@ -11,7 +11,7 @@ import {
} from "../helpers";
import { asError, getErrorMessage } from "../pure/helpers-pure";
import { pluralize } from "../pure/word";
import { redactableErrorMessage } from "../pure/errors";
import { redactableError } from "../pure/errors";
export async function runRemoteQueriesApiRequest(
credentials: Credentials,
@@ -46,10 +46,9 @@ export async function runRemoteQueriesApiRequest(
);
} else {
void showAndLogExceptionWithTelemetry(
asError(error),
redactableErrorMessage`Error submitting remote queries request: ${getErrorMessage(
error,
)}`,
redactableError(
asError(error),
)`Error submitting remote queries request: ${getErrorMessage(error)}`,
);
}
}

View File

@@ -43,7 +43,7 @@ import { DisposableObject } from "../pure/disposable-object";
import { AnalysisResults } from "./shared/analysis-result";
import { runRemoteQueriesApiRequest } from "./remote-queries-api";
import { App } from "../common/app";
import { redactableErrorMessage } from "../pure/errors";
import { redactableError } from "../pure/errors";
const autoDownloadMaxSize = 300 * 1024;
const autoDownloadMaxCount = 100;
@@ -153,18 +153,16 @@ export class RemoteQueriesManager extends DisposableObject {
noop,
(e: unknown) =>
void showAndLogExceptionWithTelemetry(
asError(e),
redactableErrorMessage`Could not open query results. ${getErrorMessage(
e,
)}`,
redactableError(
asError(e),
)`Could not open query results. ${getErrorMessage(e)}`,
),
);
} catch (e) {
void showAndLogExceptionWithTelemetry(
asError(e),
redactableErrorMessage`Could not open query results. ${getErrorMessage(
e,
)}`,
redactableError(
asError(e),
)`Could not open query results. ${getErrorMessage(e)}`,
);
}
}
@@ -283,8 +281,7 @@ export class RemoteQueriesManager extends DisposableObject {
} else if (queryWorkflowResult.status === "InProgress") {
// Should not get here. Only including this to ensure `assertNever` uses proper type checking.
void showAndLogExceptionWithTelemetry(
asError(`Unexpected status: ${queryWorkflowResult.status}`),
redactableErrorMessage`Unexpected status: ${queryWorkflowResult.status}`,
redactableError`Unexpected status: ${queryWorkflowResult.status}`,
);
} else {
// Ensure all cases are covered
@@ -491,20 +488,16 @@ export class RemoteQueriesManager extends DisposableObject {
noop,
(e: unknown) =>
void showAndLogExceptionWithTelemetry(
asError(e),
redactableErrorMessage`Could not open query results. ${getErrorMessage(
e,
)}`,
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 showAndLogExceptionWithTelemetry(
asError(
`There was an issue retrieving the result for the query [${remoteQuery.queryName}](${workflowRunUrl}).`,
),
redactableErrorMessage`There was an issue retrieving the result for the query [${remoteQuery.queryName}](${workflowRunUrl}).`,
redactableError`There was an issue retrieving the result for the query [${remoteQuery.queryName}](${workflowRunUrl}).`,
);
this.remoteQueryStatusUpdateEventEmitter.fire({
queryId,

View File

@@ -29,7 +29,7 @@ import {
VariantAnalysisScannedRepositoryState,
VariantAnalysisSubmission,
} from "./shared/variant-analysis";
import { asError, getErrorMessage } from "../pure/helpers-pure";
import { getErrorMessage } from "../pure/helpers-pure";
import { VariantAnalysisView } from "./variant-analysis-view";
import { VariantAnalysisViewManager } from "./variant-analysis-view-manager";
import {
@@ -62,7 +62,7 @@ import { URLSearchParams } from "url";
import { DbManager } from "../databases/db-manager";
import { isVariantAnalysisReposPanelEnabled } from "../config";
import { App } from "../common/app";
import { redactableErrorMessage } from "../pure/errors";
import { redactableError } from "../pure/errors";
export class VariantAnalysisManager
extends DisposableObject
@@ -263,8 +263,7 @@ export class VariantAnalysisManager
public async showView(variantAnalysisId: number): Promise<void> {
if (!this.variantAnalyses.get(variantAnalysisId)) {
void showAndLogExceptionWithTelemetry(
asError(`No variant analysis found with id: ${variantAnalysisId}.`),
redactableErrorMessage`No variant analysis found with id: ${variantAnalysisId}.`,
redactableError`No variant analysis found with id: ${variantAnalysisId}.`,
);
}
if (!this.views.has(variantAnalysisId)) {

View File

@@ -18,7 +18,7 @@ import * as appInsights from "applicationinsights";
import { extLogger } from "./common";
import { UserCancellationException } from "./commandRunner";
import { showBinaryChoiceWithUrlDialog } from "./helpers";
import { RedactableErrorMessage } from "./pure/errors";
import { RedactableError } from "./pure/errors";
// Key is injected at build time through the APP_INSIGHTS_KEY environment variable.
const key = "REPLACE-APP-INSIGHTS-KEY";
@@ -184,8 +184,7 @@ export class TelemetryListener extends ConfigListener {
}
sendError(
errorType: RedactableErrorMessage,
stack?: string,
error: RedactableError,
extraProperties?: { [key: string]: string },
) {
if (!this.reporter) {
@@ -193,11 +192,11 @@ export class TelemetryListener extends ConfigListener {
}
const properties: { [key: string]: string } = {
message: errorType.redactedMessage,
message: error.redactedMessage,
...extraProperties,
};
if (stack && stack !== "") {
properties.stack = stack;
if (error.stack && error.stack !== "") {
properties.stack = error.stack;
}
this.reporter.sendTelemetryEvent("error", properties, {});

View File

@@ -36,7 +36,7 @@ import {
import { testLogger } from "./common";
import { DatabaseItem, DatabaseManager } from "./databases";
import { asError, getErrorMessage } from "./pure/helpers-pure";
import { redactableErrorMessage } from "./pure/errors";
import { redactableError } from "./pure/errors";
/**
* Get the full path of the `.expected` file for the specified QL test.
@@ -281,8 +281,7 @@ export class QLTestAdapter extends DisposableObject implements TestAdapter {
// 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 showAndLogExceptionWithTelemetry(
asError(e),
redactableErrorMessage`Cannot remove database ${
redactableError(asError(e))`Cannot remove database ${
database.name
}: ${getErrorMessage(e)}`,
);

View File

@@ -1,44 +1,40 @@
import {
redactableErrorMessage,
RedactableErrorMessage,
} from "../../../src/pure/errors";
import { redactableError, RedactableError } from "../../../src/pure/errors";
describe("errorMessage", () => {
it("creates a RedactableErrorMessage", () => {
expect(
redactableErrorMessage`Failed to create database ${"foo"}`,
).toBeInstanceOf(RedactableErrorMessage);
it("creates a RedactableError", () => {
expect(redactableError`Failed to create database ${"foo"}`).toBeInstanceOf(
RedactableError,
);
});
it("toString() matches the given message", () => {
expect(
redactableErrorMessage`Failed to create database ${"foo"}`.toString(),
redactableError`Failed to create database ${"foo"}`.toString(),
).toEqual("Failed to create database foo");
});
it("fullMessage matches the given message", () => {
expect(
redactableErrorMessage`Failed to create database ${"foo"}`.fullMessage,
redactableError`Failed to create database ${"foo"}`.fullMessage,
).toEqual("Failed to create database foo");
});
it("redactedMessage redacts the given message", () => {
expect(
redactableErrorMessage`Failed to create database ${"foo"}`
.redactedMessage,
redactableError`Failed to create database ${"foo"}`.redactedMessage,
).toEqual("Failed to create database [REDACTED]");
});
it("fullMessage returns the correct message for nested redactableErrorMessage", () => {
it("fullMessage returns the correct message for nested redactableError", () => {
expect(
redactableErrorMessage`Failed to create database ${redactableErrorMessage`foo ${"bar"}`}`
redactableError`Failed to create database ${redactableError`foo ${"bar"}`}`
.fullMessage,
).toEqual("Failed to create database foo bar");
});
it("redactedMessage returns the correct message for nested redactableErrorMessage", () => {
it("redactedMessage returns the correct message for nested redactableError", () => {
expect(
redactableErrorMessage`Failed to create database ${redactableErrorMessage`foo ${"bar"}`}`
redactableError`Failed to create database ${redactableError`foo ${"bar"}`}`
.redactedMessage,
).toEqual("Failed to create database foo [REDACTED]");
});