Refactor error handling in data explorer Part 3 (#315)

- Make sure we pass the error message string instead of an error object when we call `TelemetryProcessor.traceFailure` since TelemetryProcessor will call `JSON.stringify` on the error object which would result in an empty object
- Removed ErrorParserUtility since it only works on specific error types. We can just log the full error message and manually derive information we need from the message.
- Added option to include stack trace in `getErrorMessage`. This is useful for figuring out where the client side script errors are coming from.
- Some minor refactors
This commit is contained in:
victor-meng 2020-11-05 20:02:57 -08:00 committed by GitHub
parent 53a8cea95e
commit 79dec6a8a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 218 additions and 267 deletions

View File

@ -15,8 +15,6 @@ src/Common/DeleteFeedback.ts
src/Common/DocumentClientUtilityBase.ts
src/Common/EditableUtility.ts
src/Common/EnvironmentUtility.ts
src/Common/ErrorParserUtility.test.ts
src/Common/ErrorParserUtility.ts
src/Common/HashMap.test.ts
src/Common/HashMap.ts
src/Common/HeadersUtility.test.ts

View File

@ -26,6 +26,10 @@ export const getErrorMessage = (error: string | Error): string => {
return replaceKnownError(errorMessage);
};
export const getErrorStack = (error: string | Error): string => {
return typeof error === "string" ? undefined : error.stack;
};
const sendNotificationForError = (errorMessage: string, errorCode: number | string): void => {
if (errorCode === HttpStatusCodes.Forbidden) {
if (errorMessage?.toLowerCase().indexOf("sharedoffer is disabled for your account") > 0) {

View File

@ -1,24 +0,0 @@
import * as ErrorParserUtility from "./ErrorParserUtility";
describe("Error Parser Utility", () => {
describe("shouldEnableCrossPartitionKeyForResourceWithPartitionKey()", () => {
it("should parse a backend error correctly", () => {
// A fake error matching what is thrown by the SDK on a bad collection create request
const innerMessage =
"The partition key component definition path '/asdwqr31 @#$#$WRadf' could not be accepted, failed near position '10'. Partition key paths must contain only valid characters and not contain a trailing slash or wildcard character.";
const message = `Message: {\"Errors\":[\"${innerMessage}\"]}\r\nActivityId: 97b2e684-7505-4921-85f6-2513b9b28220, Request URI: /apps/89fdcf25-2a0b-4d2a-aab6-e161e565b26f/services/54911149-7bb1-4e7d-a1fa-22c8b36a4bb9/partitions/cc2a7a04-5f5a-4709-bcf7-8509b264963f/replicas/132304018743619218p, RequestStats: , SDK: Microsoft.Azure.Documents.Common/2.10.0`;
const err = new Error(message) as any;
err.code = 400;
err.body = {
code: "BadRequest",
message
};
err.headers = {};
err.activityId = "97b2e684-7505-4921-85f6-2513b9b28220";
const parsedError = ErrorParserUtility.parse(err);
expect(parsedError.length).toBe(1);
expect(parsedError[0].message).toBe(innerMessage);
});
});
});

View File

@ -1,55 +0,0 @@
import * as DataModels from "../Contracts/DataModels";
export function parse(err: any): DataModels.ErrorDataModel[] {
try {
return _parse(err);
} catch (e) {
return [<DataModels.ErrorDataModel>{ message: JSON.stringify(err) }];
}
}
function _parse(err: any): DataModels.ErrorDataModel[] {
var normalizedErrors: DataModels.ErrorDataModel[] = [];
if (err.message && !err.code) {
normalizedErrors.push(err);
} else {
const innerErrors: any[] = _getInnerErrors(err.message);
normalizedErrors = innerErrors.map(innerError =>
typeof innerError === "string" ? { message: innerError } : innerError
);
}
return normalizedErrors;
}
function _getInnerErrors(message: string): any[] {
/*
The backend error message has an inner-message which is a stringified object.
For SQL errors, the "errors" property is an array of SqlErrorDataModel.
Example:
"Message: {"Errors":["Resource with specified id or name already exists"]}\r\nActivityId: 80005000008d40b6a, Request URI: /apps/19000c000c0a0005/services/mctestdocdbprod-MasterService-0-00066ab9937/partitions/900005f9000e676fb8/replicas/13000000000955p"
For non-SQL errors the "Errors" propery is an array of string.
Example:
"Message: {"errors":[{"severity":"Error","location":{"start":7,"end":8},"code":"SC1001","message":"Syntax error, incorrect syntax near '.'."}]}\r\nActivityId: d3300016d4084e310a, Request URI: /apps/12401f9e1df77/services/dc100232b1f44545/partitions/f86f3bc0001a2f78/replicas/13085003638s"
*/
let innerMessage: any = null;
const singleLineMessage = message.replace(/[\r\n]|\r|\n/g, "");
try {
// Multi-Partition error flavor
const regExp = /^(.*)ActivityId: (.*)/g;
const regString = regExp.exec(singleLineMessage);
const innerMessageString = regString[1];
innerMessage = JSON.parse(innerMessageString);
} catch (e) {
// Single-partition error flavor
const regExp = /^Message: (.*)ActivityId: (.*), Request URI: (.*)/g;
const regString = regExp.exec(singleLineMessage);
const innerMessageString = regString[1];
innerMessage = JSON.parse(innerMessageString);
}
return innerMessage.errors ? innerMessage.errors : innerMessage.Errors;
}

View File

@ -13,7 +13,6 @@ 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";
export class QueriesClient {
private static readonly PartitionKey: DataModels.PartitionKey = {
@ -97,15 +96,11 @@ export class QueriesClient {
return Promise.resolve();
},
(error: any) => {
let errorMessage: string;
const parsedError: DataModels.ErrorDataModel = ErrorParserUtility.parse(error)[0];
if (parsedError.code === HttpStatusCodes.Conflict.toString()) {
errorMessage = `Query ${query.queryName} already exists`;
} else {
errorMessage = parsedError.message;
if (error.code === HttpStatusCodes.Conflict.toString()) {
error = `Query ${query.queryName} already exists`;
}
handleError(errorMessage, "saveQuery", `Failed to save query ${query.queryName}`);
return Promise.reject(errorMessage);
handleError(error, "saveQuery", `Failed to save query ${query.queryName}`);
return Promise.reject(error);
}
)
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));

View File

@ -216,18 +216,6 @@ export interface UniqueKey {
paths: string[];
}
// Returned by DocumentDb client proxy
// Inner errors in BackendErrorDataModel when error is in SQL syntax
export interface ErrorDataModel {
message: string;
severity?: string;
location?: {
start: string;
end: string;
};
code?: string;
}
export interface CreateDatabaseAndCollectionRequest {
databaseId: string;
collectionId: string;

View File

@ -28,6 +28,7 @@ import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcesso
import SaveQueryBannerIcon from "../../../../images/save_query_banner.png";
import { QueriesClient } from "../../../Common/QueriesClient";
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
export interface QueriesGridComponentProps {
queriesClient: QueriesClient;
@ -244,7 +245,9 @@ export class QueriesGridComponent extends React.Component<QueriesGridComponentPr
databaseAccountName: container && container.databaseAccount().name,
defaultExperience: container && container.defaultExperience(),
dataExplorerArea: Constants.Areas.ContextualPane,
paneTitle: container && container.browseQueriesPane.title()
paneTitle: container && container.browseQueriesPane.title(),
error: getErrorMessage(error),
errorStack: getErrorStack(error)
},
startKey
);

View File

@ -48,7 +48,7 @@ import { IndexingPolicyComponent, IndexingPolicyComponentProps } from "./Setting
import { MongoDBCollectionResource, MongoIndex } from "../../../Utils/arm/generatedClients/2020-04-01/types";
import { readMongoDBCollectionThroughRP } from "../../../Common/dataAccess/readMongoDBCollection";
import { getIndexTransformationProgress } from "../../../Common/dataAccess/getIndexTransformationProgress";
import { getErrorMessage } from "../../../Common/ErrorHandlingUtils";
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
interface SettingsV2TabInfo {
tab: SettingsV2TabTypes;
@ -438,7 +438,8 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.props.settingsTab.tabTitle(),
error: getErrorMessage(error)
error: getErrorMessage(error),
errorStack: getErrorStack(error)
},
startKey
);
@ -560,10 +561,10 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
},
startKey
);
} catch (reason) {
} catch (error) {
this.container.isRefreshingExplorer(false);
this.props.settingsTab.isExecutionError(true);
console.error(reason);
console.error(error);
traceFailure(
Action.SettingsV2Updated,
{
@ -573,7 +574,8 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.props.settingsTab.tabTitle(),
error: reason.message
error: getErrorMessage(error),
errorStack: getErrorStack(error)
},
startKey
);

View File

@ -86,7 +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";
import { getErrorMessage, handleError, getErrorStack } from "../Common/ErrorHandlingUtils";
BindingHandlersRegisterer.registerBindingHandlers();
// Hold a reference to ComponentRegisterer to prevent transpiler to ignore import
@ -1040,11 +1040,11 @@ export default class Explorer {
);
TelemetryProcessor.traceSuccess(Action.EnableAzureSynapseLink, startTime);
this.databaseAccount(databaseAccount);
} catch (e) {
} catch (error) {
NotificationConsoleUtils.clearInProgressMessageWithId(logId);
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Enabling Azure Synapse Link for this account failed. ${e.message || JSON.stringify(e)}`
`Enabling Azure Synapse Link for this account failed. ${getErrorMessage(error)}`
);
TelemetryProcessor.traceFailure(Action.EnableAzureSynapseLink, startTime);
} finally {
@ -1451,7 +1451,8 @@ export default class Explorer {
databaseAccountName: this.databaseAccount().name,
defaultExperience: this.defaultExperience(),
dataExplorerArea: Constants.Areas.ResourceTree,
error: errorMessage
error: errorMessage,
errorStack: getErrorStack(error)
},
startKey
);
@ -1476,7 +1477,7 @@ export default class Explorer {
);
}
},
reason => {
error => {
if (resourceTreeStartKey != null) {
TelemetryProcessor.traceFailure(
Action.LoadResourceTree,
@ -1484,7 +1485,8 @@ export default class Explorer {
databaseAccountName: this.databaseAccount() && this.databaseAccount().name,
defaultExperience: this.defaultExperience && this.defaultExperience(),
dataExplorerArea: Constants.Areas.ResourceTree,
error: reason
error: getErrorMessage(error),
errorStack: getErrorStack(error)
},
resourceTreeStartKey
);
@ -1689,7 +1691,10 @@ export default class Explorer {
TelemetryProcessor.traceSuccess(Action.ResetNotebookWorkspace);
} catch (error) {
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, `Failed to reset notebook workspace: ${error}`);
TelemetryProcessor.traceFailure(Action.ResetNotebookWorkspace, error);
TelemetryProcessor.traceFailure(Action.ResetNotebookWorkspace, {
error: getErrorMessage(error),
errorStack: getErrorStack(error)
});
throw error;
} finally {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
@ -2056,7 +2061,8 @@ export default class Explorer {
databaseAccountName: this.databaseAccount() && this.databaseAccount().name,
defaultExperience: this.defaultExperience && this.defaultExperience(),
dataExplorerArea: Constants.Areas.ResourceTree,
trace: getErrorMessage(error)
error: getErrorMessage(error),
errorStack: getErrorStack(error)
},
startKey
);
@ -2718,16 +2724,17 @@ export default class Explorer {
return this.openNotebook(newFile);
})
.then(() => this.resourceTree.triggerRender())
.catch((reason: any) => {
const error = `Failed to create a new notebook: ${reason}`;
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, error);
.catch((error: any) => {
const errorMessage = `Failed to create a new notebook: ${getErrorMessage(error)}`;
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, errorMessage);
TelemetryProcessor.traceFailure(
Action.CreateNewNotebook,
{
databaseAccountName: this.databaseAccount().name,
defaultExperience: this.defaultExperience(),
dataExplorerArea: Constants.Areas.Notebook,
error
error: errorMessage,
errorStack: getErrorStack(error)
},
startKey
);

View File

@ -3,7 +3,6 @@ import * as AddCollectionUtility from "../../Shared/AddCollectionUtility";
import * as AutoPilotUtils from "../../Utils/AutoPilotUtils";
import * as Constants from "../../Common/Constants";
import * as DataModels from "../../Contracts/DataModels";
import * as ErrorParserUtility from "../../Common/ErrorParserUtility";
import * as ko from "knockout";
import * as PricingUtils from "../../Utils/PricingUtils";
import * as SharedConstants from "../../Shared/Constants";
@ -15,7 +14,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";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
export interface AddCollectionPaneOptions extends ViewModels.PaneOptions {
isPreferredApiTable: ko.Computed<boolean>;
@ -912,7 +911,8 @@ export default class AddCollectionPane extends ContextualPaneBase {
flight: this.container.flight()
},
dataExplorerArea: Constants.Areas.ContextualPane,
error: errorMessage
error: errorMessage,
errorStack: getErrorStack(error)
};
TelemetryProcessor.traceFailure(Action.CreateCollection, addCollectionPaneFailedMessage, startKey);
}

View File

@ -1,7 +1,6 @@
import * as AutoPilotUtils from "../../Utils/AutoPilotUtils";
import * as Constants from "../../Common/Constants";
import * as DataModels from "../../Contracts/DataModels";
import * as ErrorParserUtility from "../../Common/ErrorParserUtility";
import * as ko from "knockout";
import * as PricingUtils from "../../Utils/PricingUtils";
import * as SharedConstants from "../../Shared/Constants";
@ -12,6 +11,7 @@ import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstan
import { ContextualPaneBase } from "./ContextualPaneBase";
import { createDatabase } from "../../Common/dataAccess/createDatabase";
import { configContext, Platform } from "../../ConfigContext";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
export default class AddDatabasePane extends ContextualPaneBase {
public defaultExperience: ko.Computed<string>;
@ -306,8 +306,8 @@ export default class AddDatabasePane extends ContextualPaneBase {
(database: DataModels.Database) => {
this._onCreateDatabaseSuccess(offerThroughput, startKey);
},
(reason: any) => {
this._onCreateDatabaseFailure(reason, offerThroughput, reason);
(error: any) => {
this._onCreateDatabaseFailure(error, offerThroughput, startKey);
}
);
}
@ -356,11 +356,11 @@ export default class AddDatabasePane extends ContextualPaneBase {
this.resetData();
}
private _onCreateDatabaseFailure(reason: any, offerThroughput: number, startKey: number): void {
private _onCreateDatabaseFailure(error: any, offerThroughput: number, startKey: number): void {
this.isExecuting(false);
const message = ErrorParserUtility.parse(reason);
this.formErrors(message[0].message);
this.formErrorsDetails(message[0].message);
const errorMessage = getErrorMessage(error);
this.formErrors(errorMessage);
this.formErrorsDetails(errorMessage);
const addDatabasePaneFailedMessage = {
databaseAccountName: this.container.databaseAccount().name,
defaultExperience: this.container.defaultExperience(),
@ -375,7 +375,8 @@ export default class AddDatabasePane extends ContextualPaneBase {
flight: this.container.flight()
},
dataExplorerArea: Constants.Areas.ContextualPane,
error: reason
error: errorMessage,
errorStack: getErrorStack(error)
};
TelemetryProcessor.traceFailure(Action.CreateDatabase, addDatabasePaneFailedMessage, startKey);
}

View File

@ -7,7 +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";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
export class BrowseQueriesPane extends ContextualPaneBase {
public queriesGridComponentAdapter: QueriesGridComponentAdapter;
@ -69,7 +69,8 @@ export class BrowseQueriesPane extends ContextualPaneBase {
defaultExperience: this.container && this.container.defaultExperience(),
dataExplorerArea: Areas.ContextualPane,
paneTitle: this.title(),
error: errorMessage
error: errorMessage,
errorStack: getErrorStack(error)
},
startKey
);

View File

@ -13,6 +13,7 @@ import { CassandraAPIDataClient } from "../Tables/TableDataClient";
import { ContextualPaneBase } from "./ContextualPaneBase";
import { HashMap } from "../../Common/HashMap";
import { configContext, Platform } from "../../ConfigContext";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
export default class CassandraAddCollectionPane extends ContextualPaneBase {
public createTableQuery: ko.Observable<string>;
@ -429,8 +430,9 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
};
TelemetryProcessor.traceSuccess(Action.CreateCollection, addCollectionPaneSuccessMessage, startKey);
},
reason => {
this.formErrors(reason.exceptionMessage);
error => {
const errorMessage = getErrorMessage(error);
this.formErrors(errorMessage);
this.isExecuting(false);
const addCollectionPaneFailedMessage = {
databaseAccountName: this.container.databaseAccount().name,
@ -456,7 +458,8 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
toCreateKeyspace: toCreateKeyspace,
createKeyspaceQuery: createKeyspaceQuery,
createTableQuery: createTableQuery,
error: reason
error: errorMessage,
errorStack: getErrorStack(error)
};
TelemetryProcessor.traceFailure(Action.CreateCollection, addCollectionPaneFailedMessage, startKey);
}

View File

@ -3,7 +3,6 @@ import Q from "q";
import * as ViewModels from "../../Contracts/ViewModels";
import * as Constants from "../../Common/Constants";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import * as ErrorParserUtility from "../../Common/ErrorParserUtility";
import { CassandraAPIDataClient } from "../Tables/TableDataClient";
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
import { ContextualPaneBase } from "./ContextualPaneBase";
@ -12,6 +11,7 @@ import DeleteFeedback from "../../Common/DeleteFeedback";
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { deleteCollection } from "../../Common/dataAccess/deleteCollection";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
export default class DeleteCollectionConfirmationPane extends ContextualPaneBase {
public collectionIdConfirmationText: ko.Observable<string>;
@ -99,11 +99,11 @@ export default class DeleteCollectionConfirmationPane extends ContextualPaneBase
this.containerDeleteFeedback("");
}
},
(reason: any) => {
(error: any) => {
this.isExecuting(false);
const message = ErrorParserUtility.parse(reason);
this.formErrors(message[0].message);
this.formErrorsDetails(message[0].message);
const errorMessage = getErrorMessage(error);
this.formErrors(errorMessage);
this.formErrorsDetails(errorMessage);
TelemetryProcessor.traceFailure(
Action.DeleteCollection,
{
@ -111,7 +111,9 @@ export default class DeleteCollectionConfirmationPane extends ContextualPaneBase
defaultExperience: this.container.defaultExperience(),
collectionId: selectedCollection.id(),
dataExplorerArea: Constants.Areas.ContextualPane,
paneTitle: this.title()
paneTitle: this.title(),
error: errorMessage,
errorStack: getErrorStack(error)
},
startKey
);

View File

@ -3,7 +3,6 @@ import Q from "q";
import * as Constants from "../../Common/Constants";
import * as ViewModels from "../../Contracts/ViewModels";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import * as ErrorParserUtility from "../../Common/ErrorParserUtility";
import { CassandraAPIDataClient } from "../Tables/TableDataClient";
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
import { ContextualPaneBase } from "./ContextualPaneBase";
@ -14,6 +13,7 @@ import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils"
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { deleteDatabase } from "../../Common/dataAccess/deleteDatabase";
import { ARMError } from "../../Utils/arm/request";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase {
public databaseIdConfirmationText: ko.Observable<string>;
@ -108,12 +108,11 @@ export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase {
this.databaseDeleteFeedback("");
}
},
(reason: unknown) => {
(error: any) => {
this.isExecuting(false);
const message = reason instanceof ARMError ? reason.message : ErrorParserUtility.parse(reason)[0].message;
this.formErrors(message);
this.formErrorsDetails(message);
const errorMessage = getErrorMessage(error);
this.formErrors(errorMessage);
this.formErrorsDetails(errorMessage);
TelemetryProcessor.traceFailure(
Action.DeleteDatabase,
{
@ -121,7 +120,9 @@ export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase {
defaultExperience: this.container.defaultExperience(),
databaseId: selectedDatabase.id(),
dataExplorerArea: Constants.Areas.ContextualPane,
paneTitle: this.title()
paneTitle: this.title(),
error: errorMessage,
errorStack: getErrorStack(error)
},
startKey
);

View File

@ -8,7 +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";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
export class SaveQueryPane extends ContextualPaneBase {
public queryName: ko.Observable<string>;
@ -98,7 +98,8 @@ export class SaveQueryPane extends ContextualPaneBase {
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.ContextualPane,
paneTitle: this.title(),
error: errorMessage
error: errorMessage,
errorStack: getErrorStack(error)
},
startKey
);
@ -140,7 +141,8 @@ export class SaveQueryPane extends ContextualPaneBase {
defaultExperience: this.container && this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.ContextualPane,
paneTitle: this.title(),
error: errorMessage
error: errorMessage,
errorStack: getErrorStack(error)
},
startKey
);

View File

@ -6,7 +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";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
export class SetupNotebooksPane extends ContextualPaneBase {
private description: ko.Observable<string>;
@ -94,7 +94,8 @@ export class SetupNotebooksPane extends ContextualPaneBase {
defaultExperience: this.container && this.container.defaultExperience(),
dataExplorerArea: Areas.ContextualPane,
paneTitle: this.title(),
error: errorMessage
error: errorMessage,
errorStack: getErrorStack(error)
},
startKey
);

View File

@ -4,8 +4,8 @@ import * as ViewModels from "../../Contracts/ViewModels";
import { ContextualPaneBase } from "./ContextualPaneBase";
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
import * as ErrorParserUtility from "../../Common/ErrorParserUtility";
import { UploadDetailsRecord, UploadDetails } from "../../workers/upload/definitions";
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
const UPLOAD_FILE_SIZE_LIMIT = 2097152;
@ -61,9 +61,9 @@ export class UploadItemsPane extends ContextualPaneBase {
this._resetFileInput();
},
(error: any) => {
const message = ErrorParserUtility.parse(error);
this.formErrors(message[0].message);
this.formErrorsDetails(message[0].message);
const errorMessage = getErrorMessage(error);
this.formErrors(errorMessage);
this.formErrorsDetails(errorMessage);
}
)
.finally(() => {

View File

@ -1,4 +1,3 @@
import * as ErrorParserUtility from "../../Common/ErrorParserUtility";
import * as ko from "knockout";
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
import * as React from "react";
@ -9,6 +8,7 @@ import { ReactAdapter } from "../../Bindings/ReactBindingHandler";
import { UploadDetailsRecord, UploadDetails } from "../../workers/upload/definitions";
import { UploadItemsPaneComponent, UploadItemsPaneProps } from "./UploadItemsPaneComponent";
import Explorer from "../Explorer";
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
const UPLOAD_FILE_SIZE_LIMIT = 2097152;
@ -107,9 +107,9 @@ export class UploadItemsPaneAdapter implements ReactAdapter {
this.selectedFilesTitle = "";
},
error => {
const message = ErrorParserUtility.parse(error);
this.formError = message[0].message;
this.formErrorDetail = message[0].message;
const errorMessage = getErrorMessage(error);
this.formError = errorMessage;
this.formErrorDetail = errorMessage;
}
)
.finally(() => {

View File

@ -16,9 +16,9 @@ import * as Entities from "../Entities";
import QueryTablesTab from "../../Tabs/QueryTablesTab";
import * as TableEntityProcessor from "../TableEntityProcessor";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import * as ErrorParserUtility from "../../../Common/ErrorParserUtility";
import * as DataModels from "../../../Contracts/DataModels";
import * as ViewModels from "../../../Contracts/ViewModels";
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
interface IListTableEntitiesSegmentedResult extends Entities.IListTableEntitiesResult {
ExceedMaximumRetries?: boolean;
@ -387,17 +387,8 @@ export default class TableEntityListViewModel extends DataTableViewModel {
}
})
.catch((error: any) => {
const parsedErrors = ErrorParserUtility.parse(error);
var errors = parsedErrors.map((error: DataModels.ErrorDataModel) => {
return <ViewModels.QueryError>{
message: error.message,
start: error.location ? error.location.start : undefined,
end: error.location ? error.location.end : undefined,
code: error.code,
severity: error.severity
};
});
this.queryErrorMessage(errors[0].message);
const errorMessage = getErrorMessage(error);
this.queryErrorMessage(errorMessage);
if (this.queryTablesTab.onLoadStartKey != null && this.queryTablesTab.onLoadStartKey != undefined) {
TelemetryProcessor.traceFailure(
Action.Tab,
@ -408,7 +399,8 @@ export default class TableEntityListViewModel extends DataTableViewModel {
defaultExperience: this.queryTablesTab.collection.container.defaultExperience(),
dataExplorerArea: Areas.Tab,
tabTitle: this.queryTablesTab.tabTitle(),
error: error
error: errorMessage,
errorStack: getErrorStack(error)
},
this.queryTablesTab.onLoadStartKey
);

View File

@ -6,7 +6,6 @@ import * as ViewModels from "../../Contracts/ViewModels";
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
import { AccessibleVerticalList } from "../Tree/AccessibleVerticalList";
import { KeyCodes } from "../../Common/Constants";
import * as ErrorParserUtility from "../../Common/ErrorParserUtility";
import ConflictId from "../Tree/ConflictId";
import editable from "../../Common/EditableUtility";
import * as HeadersUtility from "../../Common/HeadersUtility";
@ -28,6 +27,7 @@ import {
updateDocument
} from "../../Common/DocumentClientUtilityBase";
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
export default class ConflictsTab extends TabsBase {
public selectedConflictId: ko.Observable<ConflictId>;
@ -241,9 +241,8 @@ export default class ConflictsTab extends TabsBase {
return this.loadNextPage();
}
)
.catch(reason => {
const message = ErrorParserUtility.parse(reason)[0].message;
window.alert(message);
.catch(error => {
window.alert(getErrorMessage(error));
});
}
@ -336,10 +335,10 @@ export default class ConflictsTab extends TabsBase {
);
});
},
reason => {
error => {
this.isExecutionError(true);
const message = ErrorParserUtility.parse(reason)[0].message;
window.alert(message);
const errorMessage = getErrorMessage(error);
window.alert(errorMessage);
TelemetryProcessor.traceFailure(
Action.ResolveConflict,
{
@ -349,7 +348,9 @@ export default class ConflictsTab extends TabsBase {
tabTitle: this.tabTitle(),
conflictResourceType: selectedConflict.resourceType,
conflictOperationType: selectedConflict.operationType,
conflictResourceId: selectedConflict.resourceId
conflictResourceId: selectedConflict.resourceId,
error: errorMessage,
errorStack: getErrorStack(error)
},
startKey
);
@ -396,10 +397,10 @@ export default class ConflictsTab extends TabsBase {
startKey
);
},
reason => {
error => {
this.isExecutionError(true);
const message = ErrorParserUtility.parse(reason)[0].message;
window.alert(message);
const errorMessage = getErrorMessage(error);
window.alert(errorMessage);
TelemetryProcessor.traceFailure(
Action.DeleteConflict,
{
@ -409,7 +410,9 @@ export default class ConflictsTab extends TabsBase {
tabTitle: this.tabTitle(),
conflictResourceType: selectedConflict.resourceType,
conflictOperationType: selectedConflict.operationType,
conflictResourceId: selectedConflict.resourceId
conflictResourceId: selectedConflict.resourceId,
error: errorMessage,
errorStack: getErrorStack(error)
},
startKey
);
@ -470,7 +473,8 @@ export default class ConflictsTab extends TabsBase {
defaultExperience: this.collection.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
error: error
error: getErrorMessage(error),
errorStack: getErrorStack(error)
},
this.onLoadStartKey
);
@ -545,7 +549,8 @@ export default class ConflictsTab extends TabsBase {
defaultExperience: this.collection.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
error: error
error: getErrorMessage(error),
errorStack: getErrorStack(error)
},
this.onLoadStartKey
);

View File

@ -1,7 +1,6 @@
import * as AutoPilotUtils from "../../Utils/AutoPilotUtils";
import * as Constants from "../../Common/Constants";
import * as DataModels from "../../Contracts/DataModels";
import * as ErrorParserUtility from "../../Common/ErrorParserUtility";
import * as ko from "knockout";
import * as PricingUtils from "../../Utils/PricingUtils";
import * as SharedConstants from "../../Shared/Constants";
@ -20,6 +19,7 @@ import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandBu
import { userContext } from "../../UserContext";
import { updateOfferThroughputBeyondLimit } from "../../Common/dataAccess/updateOfferThroughputBeyondLimit";
import { configContext, Platform } from "../../ConfigContext";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
const updateThroughputBeyondLimitWarningMessage: string = `
You are about to request an increase in throughput beyond the pre-allocated capacity.
@ -490,7 +490,8 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
this.container.isRefreshingExplorer(false);
this.isExecutionError(true);
console.error(error);
this.displayedError(ErrorParserUtility.parse(error)[0].message);
const errorMessage = getErrorMessage(error);
this.displayedError(errorMessage);
TelemetryProcessor.traceFailure(
Action.UpdateSettings,
{
@ -499,7 +500,8 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
error: error
error: errorMessage,
errorStack: getErrorStack(error)
},
startKey
);

View File

@ -6,7 +6,6 @@ import * as ViewModels from "../../Contracts/ViewModels";
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
import { AccessibleVerticalList } from "../Tree/AccessibleVerticalList";
import { KeyCodes } from "../../Common/Constants";
import * as ErrorParserUtility from "../../Common/ErrorParserUtility";
import DocumentId from "../Tree/DocumentId";
import editable from "../../Common/EditableUtility";
import * as HeadersUtility from "../../Common/HeadersUtility";
@ -32,7 +31,7 @@ import {
createDocument
} from "../../Common/DocumentClientUtilityBase";
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
export default class DocumentsTab extends TabsBase {
public selectedDocumentId: ko.Observable<DocumentId>;
@ -393,9 +392,8 @@ export default class DocumentsTab extends TabsBase {
const focusElement = document.getElementById("errorStatusIcon");
focusElement && focusElement.focus();
})
.catch(reason => {
const message = ErrorParserUtility.parse(reason)[0].message;
window.alert(message);
.catch(error => {
window.alert(getErrorMessage(error));
});
}
@ -475,17 +473,19 @@ export default class DocumentsTab extends TabsBase {
startKey
);
},
reason => {
error => {
this.isExecutionError(true);
const message = ErrorParserUtility.parse(reason)[0].message;
window.alert(message);
const errorMessage = getErrorMessage(error);
window.alert(errorMessage);
TelemetryProcessor.traceFailure(
Action.CreateDocument,
{
databaseAccountName: this.collection && this.collection.container.databaseAccount().name,
defaultExperience: this.collection && this.collection.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle()
tabTitle: this.tabTitle(),
error: errorMessage,
errorStack: getErrorStack(error)
},
startKey
);
@ -542,17 +542,19 @@ export default class DocumentsTab extends TabsBase {
startKey
);
},
reason => {
error => {
this.isExecutionError(true);
const message = ErrorParserUtility.parse(reason)[0].message;
window.alert(message);
const errorMessage = getErrorMessage(error);
window.alert(errorMessage);
TelemetryProcessor.traceFailure(
Action.UpdateDocument,
{
databaseAccountName: this.collection && this.collection.container.databaseAccount().name,
defaultExperience: this.collection && this.collection.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle()
tabTitle: this.tabTitle(),
error: errorMessage,
errorStack: getErrorStack(error)
},
startKey
);
@ -643,7 +645,8 @@ export default class DocumentsTab extends TabsBase {
defaultExperience: this.collection.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
error: error
error: getErrorMessage(error),
errorStack: getErrorStack(error)
},
this.onLoadStartKey
);
@ -697,16 +700,18 @@ export default class DocumentsTab extends TabsBase {
startKey
);
},
reason => {
error => {
this.isExecutionError(true);
console.error(reason);
console.error(error);
TelemetryProcessor.traceFailure(
Action.DeleteDocument,
{
databaseAccountName: this.collection && this.collection.container.databaseAccount().name,
defaultExperience: this.collection && this.collection.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle()
tabTitle: this.tabTitle(),
error: getErrorMessage(error),
errorStack: getErrorStack(error)
},
startKey
);
@ -787,7 +792,8 @@ export default class DocumentsTab extends TabsBase {
defaultExperience: this.collection.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
error: errorMessage
error: errorMessage,
errorStack: getErrorStack(error)
},
this.onLoadStartKey
);

View File

@ -4,7 +4,6 @@ import * as ko from "knockout";
import * as ViewModels from "../../Contracts/ViewModels";
import DocumentId from "../Tree/DocumentId";
import DocumentsTab from "./DocumentsTab";
import * as ErrorParserUtility from "../../Common/ErrorParserUtility";
import MongoUtility from "../../Common/MongoUtility";
import ObjectId from "../Tree/ObjectId";
import Q from "q";
@ -20,6 +19,7 @@ import {
import { extractPartitionKey } from "@azure/cosmos";
import * as Logger from "../../Common/Logger";
import { PartitionKeyDefinition } from "@azure/cosmos";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
export default class MongoDocumentsTab extends DocumentsTab {
public collection: ViewModels.Collection;
@ -72,7 +72,8 @@ export default class MongoDocumentsTab extends DocumentsTab {
databaseAccountName: this.collection && this.collection.container.databaseAccount().name,
defaultExperience: this.collection && this.collection.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle()
tabTitle: this.tabTitle(),
error: message
},
startKey
);
@ -114,17 +115,19 @@ export default class MongoDocumentsTab extends DocumentsTab {
startKey
);
},
reason => {
error => {
this.isExecutionError(true);
const message = ErrorParserUtility.parse(reason)[0].message;
window.alert(message);
const errorMessage = getErrorMessage(error);
window.alert(errorMessage);
TelemetryProcessor.traceFailure(
Action.CreateDocument,
{
databaseAccountName: this.collection && this.collection.container.databaseAccount().name,
defaultExperience: this.collection && this.collection.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle()
tabTitle: this.tabTitle(),
error: errorMessage,
errorStack: getErrorStack(error)
},
startKey
);
@ -176,17 +179,19 @@ export default class MongoDocumentsTab extends DocumentsTab {
startKey
);
},
reason => {
error => {
this.isExecutionError(true);
const message = ErrorParserUtility.parse(reason)[0].message;
window.alert(message);
const errorMessage = getErrorMessage(error);
window.alert(errorMessage);
TelemetryProcessor.traceFailure(
Action.UpdateDocument,
{
databaseAccountName: this.collection && this.collection.container.databaseAccount().name,
defaultExperience: this.collection && this.collection.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle()
tabTitle: this.tabTitle(),
error: errorMessage,
errorStack: getErrorStack(error)
},
startKey
);
@ -267,7 +272,8 @@ export default class MongoDocumentsTab extends DocumentsTab {
defaultExperience: this.collection.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
error: error
error: getErrorMessage(error),
errorStack: getErrorStack(error)
},
this.onLoadStartKey
);

View File

@ -47,7 +47,7 @@
</script>
<!-- Query Errors Tab - Start-->
<div class="active queryErrorsHeaderContainer" data-bind="visible: errors().length > 0">
<div class="active queryErrorsHeaderContainer" data-bind="visible: !!error()">
<span class="queryErrors" data-toggle="tab" data-bind="attr: { href: '#queryerrors' + tabId }">Errors</span>
</div>
<!-- Query Errors Tab - End -->
@ -56,16 +56,16 @@
<div class="queryResultErrorContentContainer">
<div
class="queryEditorWatermark"
data-bind="visible: allResultsMetadata().length === 0 && errors().length === 0 && !queryResults() && !isExecuting()"
data-bind="visible: allResultsMetadata().length === 0 && !error() && !queryResults() && !isExecuting()"
>
<p><img src="/RunQuery.png" alt="Execute Query Watermark" /></p>
<p class="queryEditorWatermarkText">Execute a query to see the results</p>
</div>
<div
class="queryResultsErrorsContent"
data-bind="visible: allResultsMetadata().length > 0 || errors().length > 0 || queryResults()"
data-bind="visible: allResultsMetadata().length > 0 || !!error() || queryResults()"
>
<div class="togglesWithMetadata" data-bind="visible: errors().length === 0">
<div class="togglesWithMetadata" data-bind="visible: !error()">
<div
class="toggles"
aria-label="Successful execution"
@ -103,12 +103,12 @@
</div>
<json-editor
params="{ content: queryResults, isReadOnly: true, ariaLabel: 'Query results' }"
data-bind="visible: queryResults().length > 0 && isResultToggled() && allResultsMetadata().length > 0 && errors().length === 0"
data-bind="visible: queryResults().length > 0 && isResultToggled() && allResultsMetadata().length > 0 && !error()"
>
</json-editor>
<div
class="queryMetricsSummaryContainer"
data-bind="visible: isMetricsToggled() && allResultsMetadata().length > 0 && errors().length === 0"
data-bind="visible: isMetricsToggled() && allResultsMetadata().length > 0 && !error()"
>
<table class="queryMetricsSummary">
<caption>
@ -308,11 +308,10 @@
id: {
href: 'queryerrors' + tabId
},
visible: errors().length > 0"
visible: !!error()"
>
<!-- ko foreach: errors -->
<div class="errorContent">
<span class="errorMessage" data-bind="text: $data.message"></span>
<span class="errorMessage" data-bind="text: error"></span>
<span class="errorDetailsLink">
<a
data-bind="click: $parent.onErrorDetailsClick, event: { keypress: $parent.onErrorDetailsKeyPress }"
@ -323,7 +322,6 @@
>
</span>
</div>
<!-- /ko -->
</div>
<!-- Query Errors Content - End-->
</div>

View File

@ -4,7 +4,6 @@ import * as Constants from "../../Common/Constants";
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
import * as ErrorParserUtility from "../../Common/ErrorParserUtility";
import TabsBase from "./TabsBase";
import { HashMap } from "../../Common/HashMap";
import * as HeadersUtility from "../../Common/HeadersUtility";
@ -18,6 +17,7 @@ import SaveQueryIcon from "../../../images/save-cosmos.svg";
import { MinimalQueryIterator } from "../../Common/IteratorUtilities";
import { queryDocuments, queryDocumentsPage } from "../../Common/DocumentClientUtilityBase";
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
enum ToggleState {
Result,
@ -34,7 +34,7 @@ export default class QueryTab extends TabsBase implements ViewModels.WaitsForTem
public selectedContent: ko.Observable<string>;
public sqlStatementToExecute: ko.Observable<string>;
public queryResults: ko.Observable<string>;
public errors: ko.ObservableArray<ViewModels.QueryError>;
public error: ko.Observable<string>;
public statusMessge: ko.Observable<string>;
public statusIcon: ko.Observable<string>;
public allResultsMetadata: ko.ObservableArray<ViewModels.QueryResultsMetadata>;
@ -82,7 +82,7 @@ export default class QueryTab extends TabsBase implements ViewModels.WaitsForTem
this.statusMessge = ko.observable<string>();
this.statusIcon = ko.observable<string>();
this.allResultsMetadata = ko.observableArray<ViewModels.QueryResultsMetadata>([]);
this.errors = ko.observableArray<ViewModels.QueryError>([]);
this.error = ko.observable<string>();
this._partitionKey = options.partitionKey;
this._resourceTokenPartitionKey = options.resourceTokenPartitionKey;
this.splitterId = this.tabId + "_splitter";
@ -266,7 +266,7 @@ export default class QueryTab extends TabsBase implements ViewModels.WaitsForTem
};
private _executeQueryDocumentsPage(firstItemIndex: number): Q.Promise<any> {
this.errors([]);
this.error("");
this.roundTrips(undefined);
if (this._iterator == null) {
const queryIteratorPromise = this._initIterator();
@ -350,27 +350,19 @@ export default class QueryTab extends TabsBase implements ViewModels.WaitsForTem
startKey
);
},
(reason: any) => {
(error: any) => {
this.isExecutionError(true);
const parsedErrors = ErrorParserUtility.parse(reason);
var errors = parsedErrors.map((error: DataModels.ErrorDataModel) => {
return <ViewModels.QueryError>{
message: error.message,
start: error.location ? error.location.start : undefined,
end: error.location ? error.location.end : undefined,
code: error.code,
severity: error.severity
};
});
this.errors(errors);
const errorMessage = getErrorMessage(error);
this.error(errorMessage);
TelemetryProcessor.traceFailure(
Action.ExecuteQuery,
{
databaseAccountName: this.collection && this.collection.container.databaseAccount().name,
defaultExperience: this.collection && this.collection.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle()
tabTitle: this.tabTitle(),
error: errorMessage,
errorStack: getErrorStack(error)
},
startKey
);

View File

@ -22,7 +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";
import { getErrorMessage, getErrorStack } 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.
@ -1175,7 +1175,8 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
error: getErrorMessage(error)
error: getErrorMessage(error),
errorStack: getErrorStack(error)
},
startKey
);

View File

@ -9,6 +9,7 @@ import ko from "knockout";
import * as Constants from "../../Common/Constants";
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
import { logConsoleError } from "../../Utils/NotificationConsoleUtils";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
export default class SettingsTabV2 extends TabsBase {
public settingsComponentAdapter: SettingsComponentAdapter;
@ -55,6 +56,7 @@ export default class SettingsTabV2 extends TabsBase {
this.isExecuting(false);
},
error => {
const errorMessage = getErrorMessage(error);
this.notification = undefined;
this.notificationRead(true);
this.isExecuting(false);
@ -67,14 +69,13 @@ export default class SettingsTabV2 extends TabsBase {
defaultExperience: this.options.collection.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle,
error: error
error: errorMessage,
errorStack: getErrorStack(error)
},
this.options.onLoadStartKey
);
logConsoleError(
`Error while fetching container settings for container ${this.options.collection.id()}: ${JSON.stringify(
error
)}`
`Error while fetching container settings for container ${this.options.collection.id()}: ${errorMessage}`
);
throw error;
}

View File

@ -13,6 +13,7 @@ import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
import StoredProcedure from "../Tree/StoredProcedure";
import ScriptTabBase from "./ScriptTabBase";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
enum ToggleState {
Result = "result",
@ -104,7 +105,7 @@ export default class StoredProcedureTab extends ScriptTabBase {
startKey
);
},
(updateError: any) => {
(error: any) => {
this.isExecutionError(true);
TelemetryProcessor.traceFailure(
Action.UpdateStoredProcedure,
@ -112,7 +113,9 @@ export default class StoredProcedureTab extends ScriptTabBase {
databaseAccountName: this.collection && this.collection.container.databaseAccount().name,
defaultExperience: this.collection && this.collection.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle()
tabTitle: this.tabTitle(),
error: getErrorMessage(error),
errorStack: getErrorStack(error)
},
startKey
);
@ -274,7 +277,9 @@ export default class StoredProcedureTab extends ScriptTabBase {
databaseAccountName: this.collection && this.collection.container.databaseAccount().name,
defaultExperience: this.collection && this.collection.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle()
tabTitle: this.tabTitle(),
error: getErrorMessage(createError),
errorStack: getErrorStack(createError)
},
startKey
);

View File

@ -8,6 +8,7 @@ import { Action } from "../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import Trigger from "../Tree/Trigger";
import ScriptTabBase from "./ScriptTabBase";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
export default class TriggerTab extends ScriptTabBase {
public collection: ViewModels.Collection;
@ -86,7 +87,9 @@ export default class TriggerTab extends ScriptTabBase {
databaseAccountName: this.collection && this.collection.container.databaseAccount().name,
defaultExperience: this.collection && this.collection.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle()
tabTitle: this.tabTitle(),
error: getErrorMessage(createError),
errorStack: getErrorStack(createError)
},
startKey
);
@ -168,7 +171,9 @@ export default class TriggerTab extends ScriptTabBase {
databaseAccountName: this.collection && this.collection.container.databaseAccount().name,
defaultExperience: this.collection && this.collection.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle()
tabTitle: this.tabTitle(),
error: getErrorMessage(createError),
errorStack: getErrorStack(createError)
},
startKey
);

View File

@ -7,6 +7,7 @@ import { Action } from "../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import UserDefinedFunction from "../Tree/UserDefinedFunction";
import ScriptTabBase from "./ScriptTabBase";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
export default class UserDefinedFunctionTab extends ScriptTabBase {
public collection: ViewModels.Collection;
@ -68,7 +69,9 @@ export default class UserDefinedFunctionTab extends ScriptTabBase {
databaseAccountName: this.collection && this.collection.container.databaseAccount().name,
defaultExperience: this.collection && this.collection.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle()
tabTitle: this.tabTitle(),
error: getErrorMessage(createError),
errorStack: getErrorStack(createError)
},
startKey
);
@ -143,7 +146,9 @@ export default class UserDefinedFunctionTab extends ScriptTabBase {
databaseAccountName: this.collection && this.collection.container.databaseAccount().name,
defaultExperience: this.collection && this.collection.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle()
tabTitle: this.tabTitle(),
error: getErrorMessage(createError),
errorStack: getErrorStack(createError)
},
startKey
);

View File

@ -40,7 +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";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
export default class Collection implements ViewModels.Collection {
public nodeKind: string;
@ -621,7 +621,8 @@ export default class Collection implements ViewModels.Collection {
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: settingsTabOptions.title,
error: errorMessage
error: errorMessage,
errorStack: getErrorStack(error)
},
startKey
);
@ -1368,7 +1369,9 @@ export default class Collection implements ViewModels.Collection {
databaseAccountName: this.container.databaseAccount().name,
databaseName: this.databaseId,
collectionName: this.id(),
defaultExperience: this.container.defaultExperience()
defaultExperience: this.container.defaultExperience(),
error: getErrorMessage(error),
errorStack: getErrorStack(error)
},
startKey
);

View File

@ -16,7 +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";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
export default class Database implements ViewModels.Database {
public nodeKind: string;
@ -99,7 +99,8 @@ export default class Database implements ViewModels.Database {
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: "Scale",
error: errorMessage
error: errorMessage,
errorStack: getErrorStack(error)
},
startKey
);