Skip to content

Commit

Permalink
add FatalError (#1231)
Browse files Browse the repository at this point in the history
  • Loading branch information
wes-carlson authored Feb 13, 2020
1 parent 435696a commit 7f09521
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 13 deletions.
1 change: 1 addition & 0 deletions BREAKING.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Any component based on runtime >= 0.14 will no longer work with loader <= 0.8
## New Error types
The following new error interfaces have been added:
- `IWriteError` is thrown when ops are sent on a read-only document
- `IFatalError` is thrown when a fatal error (500) is received from ODSP

## `IComponentContext` - `createSubComponent` removed, `createComponent` signature updated

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
IClient,
IConnect,
} from "@microsoft/fluid-protocol-definitions";
import { ThrottlingError } from "@microsoft/fluid-driver-utils";
import { FatalError, ThrottlingError } from "@microsoft/fluid-driver-utils";
import { IOdspSocketError } from "./contracts";
import { debug } from "./debug";
import { errorObjectFromOdspError, OdspNetworkError, socketErrorRetryFilter } from "./odspUtils";
Expand Down Expand Up @@ -106,7 +106,7 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection impleme
if (errorObject !== null && typeof errorObject === "object" && errorObject.canRetry) {
const socketError: IOdspSocketError = errorObject.socketError;
if (typeof socketError === "object" && socketError !== null) {
throw errorObjectFromOdspError(socketError, socketErrorRetryFilter(socketError.code));
throw errorObjectFromOdspError(socketError, socketErrorRetryFilter);
}
}
throw errorObject;
Expand Down Expand Up @@ -174,7 +174,7 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection impleme
// filter out retryable vs. non-retryable cases.
// Specifically, it will have one retry for 403 - see
// connectToDeltaStream() / getWithRetryForTokenRefresh() call.
const error = errorObjectFromOdspError(socketError, true /*canRetry */);
const error = errorObjectFromOdspError(socketError);

// The server always closes the socket after sending this message
// fully remove the socket reference now
Expand All @@ -200,7 +200,7 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection impleme
private static removeSocketIoReference(
key: string,
isFatalError: boolean,
reason: string | OdspNetworkError | ThrottlingError) {
reason: string | OdspNetworkError | ThrottlingError | FatalError) {
const socketReference = OdspDocumentDeltaConnection.socketIoSockets.get(key);
if (!socketReference) {
// This is expected to happen if we removed the reference due the socket not being connected
Expand Down
10 changes: 6 additions & 4 deletions packages/drivers/odsp-socket-storage/src/odspUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License.
*/

import { isOnline, NetworkError, ThrottlingError, OnlineStatus } from "@microsoft/fluid-driver-utils";
import { isOnline, FatalError, NetworkError, ThrottlingError, OnlineStatus } from "@microsoft/fluid-driver-utils";
import { default as fetch, RequestInfo as FetchRequestInfo, RequestInit as FetchRequestInit } from "node-fetch";
import * as sha from "sha.js";
import { IOdspSocketError } from "./contracts";
Expand Down Expand Up @@ -45,14 +45,16 @@ export class OdspNetworkError extends NetworkError {
/**
* Returns network error based on error object from ODSP socket (IOdspSocketError)
*/
export function errorObjectFromOdspError(socketError: IOdspSocketError, canRetry: boolean) {
if (socketError.retryAfter) {
export function errorObjectFromOdspError(socketError: IOdspSocketError, retryFilter?: RetryFilter) {
if (socketError.code === 500) {
return new FatalError(socketError.message);
} else if (socketError.retryAfter) {
return new ThrottlingError(socketError.message, socketError.retryAfter);
} else {
return new OdspNetworkError(
socketError.message,
socketError.code,
canRetry,
retryFilter?.(socketError.code) ?? true,
);
}
}
Expand Down
9 changes: 8 additions & 1 deletion packages/loader/driver-definitions/src/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

// tslint:disable: no-unsafe-any
export enum ErrorType {
generalError,
Expand All @@ -10,10 +11,11 @@ export enum ErrorType {
serviceError,
summarizingError,
writeError,
fatalError,
}

export type IError = IGeneralError | IThrottlingError | IConnectionError |
IServiceError | ISummarizingError | IWriteError;
IServiceError | ISummarizingError | IWriteError | IFatalError;

export interface IGeneralError {
readonly errorType: ErrorType.generalError;
Expand Down Expand Up @@ -51,3 +53,8 @@ export interface IWriteError {
readonly errorType: ErrorType.writeError;
readonly critical: boolean;
}

export interface IFatalError {
readonly errorType: ErrorType.fatalError;
readonly critical: boolean;
}
23 changes: 22 additions & 1 deletion packages/loader/driver-utils/src/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { IConnectionError, IThrottlingError, IWriteError, ErrorType } from "@microsoft/fluid-driver-definitions";

import {
IConnectionError,
IFatalError,
IThrottlingError,
IWriteError,
ErrorType,
} from "@microsoft/fluid-driver-definitions";

/**
* Network error error class - used to communicate all network errors
*/
export class NetworkError extends Error implements IConnectionError {
readonly errorType: ErrorType.connectionError = ErrorType.connectionError;

constructor(
errorMessage: string,
readonly statusCode?: number,
Expand Down Expand Up @@ -52,6 +60,19 @@ export class WriteError extends Error implements IWriteError {
}
}

export class FatalError extends Error implements IFatalError {
readonly errorType: ErrorType.fatalError = ErrorType.fatalError;
public readonly critical = true;

constructor(errorMessage: string) {
super(errorMessage);
}

public getCustomProperties() {
return copyObjectProps(this);
}
}

export function copyObjectProps(obj: object) {
const prop = {};
// Could not use {...obj} because it does not return properties of base class.
Expand Down
6 changes: 3 additions & 3 deletions packages/test/end-to-end/src/test/error.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ describe("Errors Types", () => {
message: "Test Error",
code: 400,
};
const networkError = createIError(errorObjectFromOdspError(err, false));
const networkError = createIError(errorObjectFromOdspError(err, () => false));
assert.equal(networkError.errorType, ErrorType.connectionError, "Error is not a network error");
});

Expand All @@ -104,7 +104,7 @@ describe("Errors Types", () => {
code: 529,
retryAfter: 100,
};
const throttlingError = createIError(errorObjectFromOdspError(err, true));
const throttlingError = createIError(errorObjectFromOdspError(err, () => true));
assert.equal(throttlingError.errorType, ErrorType.throttlingError, "Error is not a throttling error");
});

Expand All @@ -114,7 +114,7 @@ describe("Errors Types", () => {
code: 529,
retryAfter: 100,
};
const error1 = createIError(errorObjectFromOdspError(err, true), true);
const error1 = createIError(errorObjectFromOdspError(err, () => true), true);
const error2 = createIError(error1, false);
assert.equal(error1, error2, "Both errors should be same!!");
});
Expand Down

0 comments on commit 7f09521

Please sign in to comment.