refactor error handling part 1 (#307)

- created `getErrorMessage` function which takes in an error string or any type of error object and returns the correct error message
- replaced `error.message` with `getErrorMessage` since `error` could be a string in some cases
- merged sendNotificationForError.ts with ErrorHandlingUtils.ts
- some minor refactoring

In part 2, I will make the following changes:
 - Make `Logger.logError` function take an error message string instead of an error object. This will reduce some redundancy where the `getErrorMessage` function is being called twice (the error object passed by the caller is already an error message).
 - Update every `TelemetryProcessor.traceFailure` call to make sure we pass in an error message instead of an error object since we stringify the data we send.
This commit is contained in:
victor-meng 2020-10-30 15:09:24 -07:00 committed by GitHub
parent e2e58f73b1
commit 5741802c25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 162 additions and 159 deletions

View File

@ -1,6 +1,7 @@
import * as Cosmos from "@azure/cosmos";
import { RequestInfo, setAuthorizationTokenHeaderUsingMasterKey } from "@azure/cosmos";
import { configContext, Platform } from "../ConfigContext";
import { getErrorMessage } from "./ErrorHandlingUtils";
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
import { EmulatorMasterKey, HttpHeaders } from "./Constants";
import { userContext } from "../UserContext";
@ -69,7 +70,7 @@ export async function getTokenFromAuthService(verb: string, resourceType: string
const result = JSON.parse(await response.json());
return result;
} catch (error) {
logConsoleError(`Failed to get authorization headers for ${resourceType}: ${error.message}`);
logConsoleError(`Failed to get authorization headers for ${resourceType}: ${getErrorMessage(error)}`);
return Promise.reject(error);
}
}

View File

@ -1,11 +1,52 @@
import { CosmosError, sendNotificationForError } from "./dataAccess/sendNotificationForError";
import { HttpStatusCodes } from "./Constants";
import { MessageTypes } from "../Contracts/ExplorerContracts";
import { SubscriptionType } from "../Contracts/ViewModels";
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
import { logError } from "./Logger";
import { replaceKnownError } from "./ErrorParserUtility";
import { sendMessage } from "./MessageHandler";
export const handleError = (error: CosmosError, consoleErrorPrefix: string, area: string): void => {
const sanitizedErrorMsg = replaceKnownError(error.message);
logConsoleError(`${consoleErrorPrefix}:\n ${sanitizedErrorMsg}`);
logError(sanitizedErrorMsg, area, error.code);
sendNotificationForError(error);
export interface CosmosError {
code: number;
message?: string;
}
export const handleError = (error: string | CosmosError, consoleErrorPrefix: string, area: string): void => {
const errorMessage = getErrorMessage(error);
const errorCode = typeof error === "string" ? undefined : error.code;
// logs error to data explorer console
logConsoleError(`${consoleErrorPrefix}:\n ${errorMessage}`);
// logs error to both app insight and kusto
logError(errorMessage, area, errorCode);
// checks for errors caused by firewall and sends them to portal to handle
sendNotificationForError(errorMessage, errorCode);
};
export const getErrorMessage = (error: string | CosmosError | Error): string => {
const errorMessage = typeof error === "string" ? error : error.message;
return replaceKnownError(errorMessage);
};
const sendNotificationForError = (errorMessage: string, errorCode: number): void => {
if (errorCode === HttpStatusCodes.Forbidden) {
if (errorMessage?.toLowerCase().indexOf("sharedoffer is disabled for your account") > 0) {
return;
}
sendMessage({
type: MessageTypes.ForbiddenError,
reason: errorMessage
});
}
};
const replaceKnownError = (errorMessage: string): string => {
if (
window.dataExplorer?.subscriptionType() === SubscriptionType.Internal &&
errorMessage.indexOf("SharedOffer is Disabled for your account") >= 0
) {
return "Database throughput is not supported for internal subscriptions.";
} else if (errorMessage.indexOf("Partition key paths must contain only valid") >= 0) {
return "Partition key paths must contain only valid characters and not contain a trailing slash or wildcard character.";
}
return errorMessage;
};

View File

@ -1,18 +1,4 @@
import * as DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels";
export function replaceKnownError(err: string): string {
if (
window.dataExplorer.subscriptionType() === ViewModels.SubscriptionType.Internal &&
err.indexOf("SharedOffer is Disabled for your account") >= 0
) {
return "Database throughput is not supported for internal subscriptions.";
} else if (err.indexOf("Partition key paths must contain only valid") >= 0) {
return "Partition key paths must contain only valid characters and not contain a trailing slash or wildcard character.";
}
return err;
}
export function parse(err: any): DataModels.ErrorDataModel[] {
try {

View File

@ -1,3 +1,4 @@
import { CosmosError, getErrorMessage } from "./ErrorHandlingUtils";
import { sendMessage } from "./MessageHandler";
import { Diagnostics, MessageTypes } from "../Contracts/ExplorerContracts";
import { appInsights } from "../Shared/appInsights";
@ -21,14 +22,9 @@ export function logWarning(message: string, area: string, code?: number): void {
return _logEntry(entry);
}
export function logError(message: string | Error, area: string, code?: number): void {
let logMessage: string;
if (typeof message === "string") {
logMessage = message;
} else {
logMessage = JSON.stringify(message, Object.getOwnPropertyNames(message));
}
const entry: Diagnostics.LogEntry = _generateLogEntry(Diagnostics.LogEntryLevel.Error, logMessage, area, code);
export function logError(error: string | CosmosError | Error, area: string, code?: number): void {
const errorMessage: string = getErrorMessage(error);
const entry: Diagnostics.LogEntry = _generateLogEntry(Diagnostics.LogEntryLevel.Error, errorMessage, area, code);
return _logEntry(entry);
}

View File

@ -12,6 +12,7 @@ import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants";
import { userContext } from "../UserContext";
import { createDocument, deleteDocument, queryDocuments, queryDocumentsPage } from "./DocumentClientUtilityBase";
import { createCollection } from "./dataAccess/createCollection";
import { handleError } from "./ErrorHandlingUtils";
import * as ErrorParserUtility from "./ErrorParserUtility";
import * as Logger from "./Logger";
@ -53,13 +54,8 @@ export class QueriesClient {
return Promise.resolve(collection);
},
(error: any) => {
const stringifiedError: string = error.message;
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to set up account for saving queries: ${stringifiedError}`
);
Logger.logError(stringifiedError, "setupQueriesCollection");
return Promise.reject(stringifiedError);
handleError(error, "Failed to set up account for saving queries", "setupQueriesCollection");
return Promise.reject(error);
}
)
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
@ -163,25 +159,15 @@ export class QueriesClient {
return Promise.resolve(queries);
},
(error: any) => {
const stringifiedError: string = error.message;
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to fetch saved queries: ${stringifiedError}`
);
Logger.logError(stringifiedError, "getSavedQueries");
return Promise.reject(stringifiedError);
handleError(error, "Failed to fetch saved queries", "getSavedQueries");
return Promise.reject(error);
}
);
},
(error: any) => {
// should never get into this state but we handle this regardless
const stringifiedError: string = error.message;
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to fetch saved queries: ${stringifiedError}`
);
Logger.logError(stringifiedError, "getSavedQueries");
return Promise.reject(stringifiedError);
handleError(error, "Failed to fetch saved queries", "getSavedQueries");
return Promise.reject(error);
}
)
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
@ -232,13 +218,8 @@ export class QueriesClient {
return Promise.resolve();
},
(error: any) => {
const stringifiedError: string = error.message;
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to delete query ${query.queryName}: ${stringifiedError}`
);
Logger.logError(stringifiedError, "deleteQuery");
return Promise.reject(stringifiedError);
handleError(error, `Failed to delete query ${query.queryName}`, "deleteQuery");
return Promise.reject(error);
}
)
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));

View File

@ -7,9 +7,8 @@ import {
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { client } from "../CosmosClient";
import { createUpdateSqlTrigger, getSqlTrigger } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { userContext } from "../../UserContext";
export async function createTrigger(
@ -66,9 +65,7 @@ export async function createTrigger(
.scripts.triggers.create(trigger);
return response.resource;
} catch (error) {
logConsoleError(`Error while creating trigger ${trigger.id}:\n ${error.message}`);
logError(error.message, "CreateTrigger", error.code);
sendNotificationForError(error);
handleError(error, `Error while creating trigger ${trigger.id}`, "CreateTrigger");
throw error;
} finally {
clearMessage();

View File

@ -1,7 +1,7 @@
import { Offer } from "../../Contracts/DataModels";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";
import { handleError, getErrorMessage } from "../ErrorHandlingUtils";
export const readOffers = async (): Promise<Offer[]> => {
const clearMessage = logConsoleProgress(`Querying offers`);
@ -13,7 +13,7 @@ export const readOffers = async (): Promise<Offer[]> => {
return response?.resources;
} catch (error) {
// This should be removed when we can correctly identify if an account is serverless when connected using connection string too.
if (error.message.includes("Reading or replacing offers is not supported for serverless accounts")) {
if (getErrorMessage(error)?.includes("Reading or replacing offers is not supported for serverless accounts")) {
return [];
}

View File

@ -1,20 +0,0 @@
import * as Constants from "../Constants";
import { sendMessage } from "../MessageHandler";
import { MessageTypes } from "../../Contracts/ExplorerContracts";
export interface CosmosError {
code: number;
message?: string;
}
export function sendNotificationForError(error: CosmosError): void {
if (error && error.code === Constants.HttpStatusCodes.Forbidden) {
if (error.message && error.message.toLowerCase().indexOf("sharedoffer is disabled for your account") > 0) {
return;
}
sendMessage({
type: MessageTypes.ForbiddenError,
reason: error && error.message ? error.message : error
});
}
}

View File

@ -1,8 +1,9 @@
import { Platform, configContext } from "../../ConfigContext";
import { getAuthorizationHeader } from "../../Utils/AuthorizationUtils";
import { AutoPilotOfferSettings } from "../../Contracts/DataModels";
import { logConsoleProgress, logConsoleInfo, logConsoleError } from "../../Utils/NotificationConsoleUtils";
import { logConsoleProgress, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
import { HttpHeaders } from "../Constants";
import { handleError } from "../ErrorHandlingUtils";
interface UpdateOfferThroughputRequest {
subscriptionId: string;
@ -44,8 +45,13 @@ export async function updateOfferThroughputBeyondLimit(request: UpdateOfferThrou
clearMessage();
return undefined;
}
const error = await response.json();
logConsoleError(`Failed to request an increase in throughput for ${request.throughput}: ${error.message}`);
handleError(
error,
`Failed to request an increase in throughput for ${request.throughput}`,
"updateOfferThroughputBeyondLimit"
);
clearMessage();
throw new Error(error.message);
throw error;
}

View File

@ -50,6 +50,7 @@ import {
getMongoDBCollectionIndexTransformationProgress,
readMongoDBCollectionThroughRP
} from "../../../Common/dataAccess/readMongoDBCollection";
import { getErrorMessage } from "../../../Common/ErrorHandlingUtils";
interface SettingsV2TabInfo {
tab: SettingsV2TabTypes;
@ -437,7 +438,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.props.settingsTab.tabTitle(),
error: error.message
error: getErrorMessage(error)
},
startKey
);

View File

@ -86,6 +86,7 @@ import { CommandButtonComponentProps } from "./Controls/CommandButton/CommandBut
import { updateUserContext, userContext } from "../UserContext";
import { stringToBlob } from "../Utils/BlobUtils";
import { IChoiceGroupProps } from "office-ui-fabric-react";
import { getErrorMessage, handleError } from "../Common/ErrorHandlingUtils";
BindingHandlersRegisterer.registerBindingHandlers();
// Hold a reference to ComponentRegisterer to prevent transpiler to ignore import
@ -1112,7 +1113,7 @@ export default class Explorer {
);
this.renewExplorerShareAccess(this, this.tokenForRenewal())
.fail((error: any) => {
const stringifiedError: string = error.message;
const stringifiedError: string = getErrorMessage(error);
this.renewTokenError("Invalid connection string specified");
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
@ -1141,7 +1142,7 @@ export default class Explorer {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to generate share url: ${error.message}`
`Failed to generate share url: ${getErrorMessage(error)}`
);
console.error(error);
}
@ -1166,7 +1167,10 @@ export default class Explorer {
deferred.resolve();
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, `Failed to connect: ${error.message}`);
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to connect: ${getErrorMessage(error)}`
);
deferred.reject(error);
}
)
@ -1440,19 +1444,20 @@ export default class Explorer {
this._setLoadingStatusText("Failed to fetch databases.");
this.isRefreshingExplorer(false);
deferred.reject(error);
const errorMessage = getErrorMessage(error);
TelemetryProcessor.traceFailure(
Action.LoadDatabases,
{
databaseAccountName: this.databaseAccount().name,
defaultExperience: this.defaultExperience(),
dataExplorerArea: Constants.Areas.ResourceTree,
error: error.message
error: errorMessage
},
startKey
);
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while refreshing databases: ${error.message}`
`Error while refreshing databases: ${errorMessage}`
);
}
);
@ -1554,8 +1559,7 @@ export default class Explorer {
return Promise.all(sparkPromises).then(() => workspaceItems);
} catch (error) {
Logger.logError(error, "Explorer/this._arcadiaManager.listWorkspacesAsync");
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, error.message);
handleError(error, "Get Arcadia workspaces failed", "Explorer/this._arcadiaManager.listWorkspacesAsync");
return Promise.resolve([]);
}
}
@ -1590,10 +1594,10 @@ export default class Explorer {
);
} catch (error) {
this._isInitializingNotebooks = false;
Logger.logError(error, "initNotebooks/getNotebookConnectionInfoAsync");
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to get notebook workspace connection info: ${error.message}`
handleError(
error,
`Failed to get notebook workspace connection info: ${getErrorMessage(error)}`,
"initNotebooks/getNotebookConnectionInfoAsync"
);
throw error;
} finally {
@ -1669,8 +1673,7 @@ export default class Explorer {
await this.notebookWorkspaceManager.startNotebookWorkspaceAsync(this.databaseAccount().id, "default");
}
} catch (error) {
Logger.logError(error, "Explorer/ensureNotebookWorkspaceRunning");
NotificationConsoleUtils.logConsoleError(`Failed to initialize notebook workspace: ${error.message}`);
handleError(error, "Failed to initialize notebook workspace", "Explorer/ensureNotebookWorkspaceRunning");
} finally {
clearMessage && clearMessage();
}
@ -2052,7 +2055,7 @@ export default class Explorer {
databaseAccountName: this.databaseAccount() && this.databaseAccount().name,
defaultExperience: this.defaultExperience && this.defaultExperience(),
dataExplorerArea: Constants.Areas.ResourceTree,
trace: error.message
trace: getErrorMessage(error)
},
startKey
);
@ -2514,7 +2517,7 @@ export default class Explorer {
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Could not download notebook ${error.message}`
`Could not download notebook ${getErrorMessage(error)}`
);
clearMessage();

View File

@ -29,6 +29,7 @@ import { InputProperty } from "../../../Contracts/ViewModels";
import { QueryIterator, ItemDefinition, Resource } from "@azure/cosmos";
import LoadingIndicatorIcon from "../../../../images/LoadingIndicator_3Squares.gif";
import { queryDocuments, queryDocumentsPage } from "../../../Common/DocumentClientUtilityBase";
import { getErrorMessage } from "../../../Common/ErrorHandlingUtils";
export interface GraphAccessor {
applyFilter: () => void;
@ -892,7 +893,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
backendPromise.then(
(result: UserQueryResult) => (this.queryTotalRequestCharge = result.requestCharge),
(error: any) => {
const errorMsg = `Failure in submitting query: ${query}: ${error.message}`;
const errorMsg = `Failure in submitting query: ${query}: ${getErrorMessage(error)}`;
GraphExplorer.reportToConsole(ConsoleDataType.Error, errorMsg);
this.setState({
filterQueryError: errorMsg
@ -1826,7 +1827,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
promise
.then((result: GremlinClient.GremlinRequestResult) => this.processGremlinQueryResults(result))
.catch((error: any) => {
const errorMsg = `Failed to process query result: ${error.message}`;
const errorMsg = `Failed to process query result: ${getErrorMessage(error)}`;
GraphExplorer.reportToConsole(ConsoleDataType.Error, errorMsg);
this.setState({
filterQueryError: errorMsg

View File

@ -8,6 +8,7 @@ import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUti
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
import { HashMap } from "../../../Common/HashMap";
import * as Logger from "../../../Common/Logger";
import { getErrorMessage } from "../../../Common/ErrorHandlingUtils";
export interface GremlinClientParameters {
endpoint: string;
@ -58,14 +59,11 @@ export class GremlinClient {
}
},
failureCallback: (result: Result, error: any) => {
if (typeof error !== "string") {
error = error.message;
}
const errorMessage = getErrorMessage(error);
const requestId = result.requestId;
if (!requestId || !this.pendingResults.has(requestId)) {
const msg = `Error: ${error}, unknown requestId:${requestId} ${GremlinClient.getRequestChargeString(
const msg = `Error: ${errorMessage}, unknown requestId:${requestId} ${GremlinClient.getRequestChargeString(
result.requestCharge
)}`;
GremlinClient.reportError(msg);
@ -73,11 +71,11 @@ export class GremlinClient {
// Fail all pending requests if no request id (fatal)
if (!requestId) {
this.pendingResults.keys().forEach((reqId: string) => {
this.abortPendingRequest(reqId, error, null);
this.abortPendingRequest(reqId, errorMessage, null);
});
}
} else {
this.abortPendingRequest(requestId, error, result.requestCharge);
this.abortPendingRequest(requestId, errorMessage, result.requestCharge);
}
},
infoCallback: (msg: string) => {

View File

@ -15,6 +15,7 @@ import { configContext, Platform } from "../../ConfigContext";
import { ContextualPaneBase } from "./ContextualPaneBase";
import { DynamicListItem } from "../Controls/DynamicList/DynamicListComponent";
import { createCollection } from "../../Common/dataAccess/createCollection";
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
export interface AddCollectionPaneOptions extends ViewModels.PaneOptions {
isPreferredApiTable: ko.Computed<boolean>;
@ -881,10 +882,9 @@ export default class AddCollectionPane extends ContextualPaneBase {
this.resetData();
this.container.refreshAllDatabases();
},
(reason: any) => {
(error: any) => {
this.isExecuting(false);
const message = ErrorParserUtility.parse(reason);
const errorMessage = ErrorParserUtility.replaceKnownError(message[0].message);
const errorMessage: string = getErrorMessage(error);
this.formErrors(errorMessage);
this.formErrorsDetails(errorMessage);
const addCollectionPaneFailedMessage = {
@ -912,7 +912,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
flight: this.container.flight()
},
dataExplorerArea: Constants.Areas.ContextualPane,
error: reason
error: errorMessage
};
TelemetryProcessor.traceFailure(Action.CreateCollection, addCollectionPaneFailedMessage, startKey);
}

View File

@ -7,6 +7,7 @@ import * as Logger from "../../Common/Logger";
import { QueriesGridComponentAdapter } from "../Controls/QueriesGridReactComponent/QueriesGridComponentAdapter";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import QueryTab from "../Tabs/QueryTab";
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
export class BrowseQueriesPane extends ContextualPaneBase {
public queriesGridComponentAdapter: QueriesGridComponentAdapter;
@ -60,17 +61,19 @@ export class BrowseQueriesPane extends ContextualPaneBase {
startKey
);
} catch (error) {
const errorMessage = getErrorMessage(error);
TelemetryProcessor.traceFailure(
Action.SetupSavedQueries,
{
databaseAccountName: this.container && this.container.databaseAccount().name,
defaultExperience: this.container && this.container.defaultExperience(),
dataExplorerArea: Areas.ContextualPane,
paneTitle: this.title()
paneTitle: this.title(),
error: errorMessage
},
startKey
);
this.formErrors(`Failed to setup a collection for saved queries: ${error.message}`);
this.formErrors(`Failed to setup a collection for saved queries: ${errorMessage}`);
} finally {
this.isExecuting(false);
}

View File

@ -7,6 +7,7 @@ import { ContextualPaneBase } from "./ContextualPaneBase";
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility";
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
export class RenewAdHocAccessPane extends ContextualPaneBase {
public accessKey: ko.Observable<string>;
@ -82,7 +83,7 @@ export class RenewAdHocAccessPane extends ContextualPaneBase {
this.container
.renewShareAccess(this.accessKey())
.fail((error: any) => {
const errorMessage: string = error.message;
const errorMessage: string = getErrorMessage(error);
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, `Failed to connect: ${errorMessage}`);
this.formErrors(errorMessage);
this.formErrorsDetails(errorMessage);

View File

@ -8,6 +8,7 @@ import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsol
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import QueryTab from "../Tabs/QueryTab";
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
export class SaveQueryPane extends ContextualPaneBase {
public queryName: ko.Observable<string>;
@ -87,18 +88,17 @@ export class SaveQueryPane extends ContextualPaneBase {
},
(error: any) => {
this.isExecuting(false);
if (typeof error != "string") {
error = error.message;
}
const errorMessage = getErrorMessage(error);
this.formErrors("Failed to save query");
this.formErrorsDetails(`Failed to save query: ${error}`);
this.formErrorsDetails(`Failed to save query: ${errorMessage}`);
TelemetryProcessor.traceFailure(
Action.SaveQuery,
{
databaseAccountName: this.container.databaseAccount().name,
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.ContextualPane,
paneTitle: this.title()
paneTitle: this.title(),
error: errorMessage
},
startKey
);
@ -132,18 +132,20 @@ export class SaveQueryPane extends ContextualPaneBase {
startKey
);
} catch (error) {
const errorMessage = getErrorMessage(error);
TelemetryProcessor.traceFailure(
Action.SetupSavedQueries,
{
databaseAccountName: this.container && this.container.databaseAccount().name,
defaultExperience: this.container && this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.ContextualPane,
paneTitle: this.title()
paneTitle: this.title(),
error: errorMessage
},
startKey
);
this.formErrors("Failed to setup a container for saved queries");
this.formErrors(`Failed to setup a container for saved queries: ${error.message}`);
this.formErrorsDetails(`Failed to setup a container for saved queries: ${errorMessage}`);
} finally {
this.isExecuting(false);
}

View File

@ -6,6 +6,7 @@ import { ContextualPaneBase } from "./ContextualPaneBase";
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import * as ko from "knockout";
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
export class SetupNotebooksPane extends ContextualPaneBase {
private description: ko.Observable<string>;
@ -85,7 +86,7 @@ export class SetupNotebooksPane extends ContextualPaneBase {
"Successfully created a default notebook workspace for the account"
);
} catch (error) {
const errorMessage = typeof error == "string" ? error : error.message;
const errorMessage = getErrorMessage(error);
TelemetryProcessor.traceFailure(
Action.CreateNotebookWorkspace,
{

View File

@ -32,6 +32,7 @@ import {
createDocument
} from "../../Common/DocumentClientUtilityBase";
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
export default class DocumentsTab extends TabsBase {
public selectedDocumentId: ko.Observable<DocumentId>;
@ -774,10 +775,8 @@ export default class DocumentsTab extends TabsBase {
},
error => {
this.isExecutionError(true);
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
typeof error === "string" ? error : error.message
);
const errorMessage = getErrorMessage(error);
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, errorMessage);
if (this.onLoadStartKey != null && this.onLoadStartKey != undefined) {
TelemetryProcessor.traceFailure(
Action.Tab,
@ -788,7 +787,7 @@ export default class DocumentsTab extends TabsBase {
defaultExperience: this.collection.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
error: error
error: errorMessage
},
this.onLoadStartKey
);

View File

@ -22,6 +22,7 @@ import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandBu
import { userContext } from "../../UserContext";
import { updateOfferThroughputBeyondLimit } from "../../Common/dataAccess/updateOfferThroughputBeyondLimit";
import { configContext, Platform } from "../../ConfigContext";
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
const ttlWarning: string = `
The system will automatically delete items based on the TTL value (in seconds) you provide, without needing a delete operation explicitly issued by a client application.
@ -1174,7 +1175,7 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
error: error.message
error: getErrorMessage(error)
},
startKey
);

View File

@ -40,6 +40,7 @@ import Explorer from "../Explorer";
import { userContext } from "../../UserContext";
import TabsBase from "../Tabs/TabsBase";
import { fetchPortalNotifications } from "../../Common/PortalNotifications";
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
export default class Collection implements ViewModels.Collection {
public nodeKind: string;
@ -610,6 +611,7 @@ export default class Collection implements ViewModels.Collection {
settingsTab.pendingNotification(pendingNotification);
},
(error: any) => {
const errorMessage = getErrorMessage(error);
TelemetryProcessor.traceFailure(
Action.Tab,
{
@ -619,13 +621,13 @@ export default class Collection implements ViewModels.Collection {
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: settingsTabOptions.title,
error: error
error: errorMessage
},
startKey
);
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while fetching container settings for container ${this.id()}: ${error.message}`
`Error while fetching container settings for container ${this.id()}: ${errorMessage}`
);
throw error;
}
@ -869,7 +871,7 @@ export default class Collection implements ViewModels.Collection {
collectionName: this.id(),
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.ResourceTree,
error: typeof error === "string" ? error : error.message
error: getErrorMessage(error)
});
}
);
@ -928,7 +930,7 @@ export default class Collection implements ViewModels.Collection {
collectionName: this.id(),
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.ResourceTree,
error: typeof error === "string" ? error : error.message
error: getErrorMessage(error)
});
}
);
@ -988,7 +990,7 @@ export default class Collection implements ViewModels.Collection {
collectionName: this.id(),
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.ResourceTree,
error: typeof error === "string" ? error : error.message
error: getErrorMessage(error)
});
}
);
@ -1185,7 +1187,7 @@ export default class Collection implements ViewModels.Collection {
},
error => {
record.numFailed++;
record.errors = [...record.errors, error.message];
record.errors = [...record.errors, getErrorMessage(error)];
return Q.resolve();
}
);
@ -1238,7 +1240,7 @@ export default class Collection implements ViewModels.Collection {
(error: any) => {
Logger.logError(
JSON.stringify({
error: error.message,
error: getErrorMessage(error),
accountName: this.container && this.container.databaseAccount(),
databaseName: this.databaseId,
collectionName: this.id()

View File

@ -16,6 +16,7 @@ import { readCollections } from "../../Common/dataAccess/readCollections";
import { readDatabaseOffer } from "../../Common/dataAccess/readDatabaseOffer";
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
import { fetchPortalNotifications } from "../../Common/PortalNotifications";
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
export default class Database implements ViewModels.Database {
public nodeKind: string;
@ -88,6 +89,7 @@ export default class Database implements ViewModels.Database {
this.container.tabsManager.activateNewTab(settingsTab);
},
(error: any) => {
const errorMessage = getErrorMessage(error);
TelemetryProcessor.traceFailure(
Action.Tab,
{
@ -97,13 +99,13 @@ export default class Database implements ViewModels.Database {
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: "Scale",
error: error
error: errorMessage
},
startKey
);
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while fetching database settings for database ${this.id()}: ${error.message}`
`Error while fetching database settings for database ${this.id()}: ${errorMessage}`
);
throw error;
}
@ -239,7 +241,7 @@ export default class Database implements ViewModels.Database {
(error: any) => {
Logger.logError(
JSON.stringify({
error: error.message,
error: getErrorMessage(error),
accountName: this.container && this.container.databaseAccount(),
databaseName: this.id(),
collectionName: this.id()

View File

@ -9,6 +9,7 @@ import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import Explorer from "../Explorer";
import StoredProcedureTab from "../Tabs/StoredProcedureTab";
import TabsBase from "../Tabs/TabsBase";
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
const sampleStoredProcedureBody: string = `// SAMPLE STORED PROCEDURE
function sample(prefix) {
@ -158,7 +159,7 @@ export default class StoredProcedure {
sprocTab.onExecuteSprocsResult(result, result.scriptLogs);
},
(error: any) => {
sprocTab.onExecuteSprocsError(error.message);
sprocTab.onExecuteSprocsError(getErrorMessage(error));
}
)
.finally(() => {

View File

@ -9,6 +9,7 @@ import { NotebookUtil } from "../Explorer/Notebook/NotebookUtil";
import { GitHubClient, IGitHubFile, IGitHubResponse } from "./GitHubClient";
import * as GitHubUtils from "../Utils/GitHubUtils";
import UrlUtility from "../Common/UrlUtility";
import { getErrorMessage } from "../Common/ErrorHandlingUtils";
export interface GitHubContentProviderParams {
gitHubClient: GitHubClient;
@ -423,7 +424,7 @@ export class GitHubContentProvider implements IContentProvider {
request: {},
status: error.errno,
response: error,
responseText: error.message,
responseText: getErrorMessage(error),
responseType: "json"
};
}

View File

@ -21,6 +21,7 @@ import {
import { DialogComponentAdapter } from "./Explorer/Controls/DialogReactComponent/DialogComponentAdapter";
import { DialogProps } from "./Explorer/Controls/DialogReactComponent/DialogComponent";
import { DirectoryListProps } from "./Explorer/Controls/Directory/DirectoryListComponent";
import { getErrorMessage } from "./Common/ErrorHandlingUtils";
import { initializeIcons } from "office-ui-fabric-react/lib/Icons";
import { LocalStorageUtility, StorageKey, SessionStorageUtility } from "./Shared/StorageUtility";
import * as Logger from "./Common/Logger";
@ -509,12 +510,13 @@ class HostedExplorer {
}
});
} catch (error) {
Logger.logError(error, "HostedExplorer/_getArcadiaToken");
const errorMessage = getErrorMessage(error);
Logger.logError(errorMessage, "HostedExplorer/_getArcadiaToken");
this._sendMessageToExplorerFrame({
actionType: ActionType.TransmitCachedData,
message: {
id: message && message.id,
error: error.message
error: errorMessage
}
});
}
@ -559,12 +561,9 @@ class HostedExplorer {
});
},
error => {
if (typeof error !== "string") {
error = JSON.stringify(error, Object.getOwnPropertyNames(error));
}
this._sendMessageToExplorerFrame({
type: MessageTypes.GetAccessAadResponse,
error
error: getErrorMessage(error)
});
}
);
@ -1008,7 +1007,7 @@ class HostedExplorer {
return accounts;
} catch (error) {
this._logConsoleMessage(ConsoleDataType.Error, `Failed to fetch accounts: ${error.message}`);
this._logConsoleMessage(ConsoleDataType.Error, `Failed to fetch accounts: ${getErrorMessage(error)}`);
this._clearInProgressMessageWithId(id);
throw error;
@ -1047,7 +1046,7 @@ class HostedExplorer {
displayText: "Error loading account"
});
this._updateLoadingStatusText(`Failed to load selected account: ${newAccount.name}`);
this._logConsoleMessage(ConsoleDataType.Error, `Failed to connect: ${error.message}`);
this._logConsoleMessage(ConsoleDataType.Error, `Failed to connect: ${getErrorMessage(error)}`);
this._clearInProgressMessageWithId(id);
throw error;
}

View File

@ -23,6 +23,7 @@ import "../../Explorer/Tables/DataTable/DataTableBindingManager";
import Explorer from "../../Explorer/Explorer";
import { updateUserContext } from "../../UserContext";
import { configContext } from "../../ConfigContext";
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
export default class Main {
private static _databaseAccountId: string;
@ -245,7 +246,7 @@ export default class Main {
);
},
(error: any) => {
deferred.reject(`Failed to generate encrypted token: ${error.message}`);
deferred.reject(`Failed to generate encrypted token: ${getErrorMessage(error)}`);
}
);

View File

@ -3,6 +3,7 @@ import { DocumentClientParams, UploadDetailsRecord, UploadDetails } from "./defi
import { client } from "../../Common/CosmosClient";
import { configContext, updateConfigContext } from "../../ConfigContext";
import { updateUserContext } from "../../UserContext";
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
let numUploadsSuccessful = 0;
let numUploadsFailed = 0;
@ -93,7 +94,7 @@ function createDocumentsFromFile(fileName: string, documentContent: string): voi
})
.catch(error => {
console.error(error);
recordUploadDetailErrorForFile(fileName, error.message);
recordUploadDetailErrorForFile(fileName, getErrorMessage(error));
numUploadsFailed++;
})
.finally(() => {

View File

@ -15,13 +15,11 @@
"./src/Common/DeleteFeedback.ts",
"./src/Common/HashMap.ts",
"./src/Common/HeadersUtility.ts",
"./src/Common/Logger.ts",
"./src/Common/MessageHandler.ts",
"./src/Common/MongoUtility.ts",
"./src/Common/ObjectCache.ts",
"./src/Common/ThemeUtility.ts",
"./src/Common/UrlUtility.ts",
"./src/Common/dataAccess/sendNotificationForError.ts",
"./src/ConfigContext.ts",
"./src/Contracts/ActionContracts.ts",
"./src/Contracts/DataModels.ts",