Expose new UsageError in DriverErrorType (#10950)

[AB#962](https://dev.azure.com/fluidframework/internal/_workitems/edit/962)

For more information about how to contribute to this repo, visit [this page](https://github.com/microsoft/FluidFramework/blob/main/CONTRIBUTING.md).

## Description

> We should not use strings, instead use values from one of the enums. Given that errors thrown is part of API contract (see such things are Container.close(), "closed" event), we need to ensure that clients can enumerate all known errorTypes different layers of our stack can throw. Anything unknown (i.e. not having shape of error our framework throws) is wrapped into generic error somewhere, so we maintain contract that client can enumerate all error types at complication time (assuming no dynamic code loading, of course).

See https://github.com/microsoft/FluidFramework/pull/10692#discussion_r905243919

**Note**: This PR also makes simplifications around `DeltaStreamConnectionForbiddenError` and `container-utils>UsageError`
This commit is contained in:
Kian Thompson 2022-07-25 15:59:39 -07:00 коммит произвёл GitHub
Родитель 23c55c777e
Коммит 98f1ffa777
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
15 изменённых файлов: 67 добавлений и 34 удалений

Просмотреть файл

@ -11,6 +11,7 @@ import { ISequencedDocumentMessage } from '@fluidframework/protocol-definitions'
import { ITelemetryLogger } from '@fluidframework/common-definitions';
import { ITelemetryProperties } from '@fluidframework/common-definitions';
import { IThrottlingWarning } from '@fluidframework/container-definitions';
import { IUsageError } from '@fluidframework/container-definitions';
import { LoggingError } from '@fluidframework/telemetry-utils';
// @public
@ -70,10 +71,10 @@ export class ThrottlingWarning extends LoggingError implements IThrottlingWarnin
}
// @public
export class UsageError extends LoggingError implements IFluidErrorBase {
export class UsageError extends LoggingError implements IUsageError, IFluidErrorBase {
constructor(message: string);
// (undocumented)
readonly errorType = "usageError";
readonly errorType = ContainerErrorType.usageError;
}
// (No @packageDocumentation comment for this package)

Просмотреть файл

@ -42,6 +42,7 @@ export enum DriverErrorType {
throttlingError = "throttlingError",
// (undocumented)
unsupportedClientProtocolVersion = "unsupportedClientProtocolVersion",
usageError = "usageError",
writeError = "writeError"
}
@ -181,7 +182,7 @@ export interface IDocumentStorageServicePolicies {
// @public
export interface IDriverBasicError extends IDriverErrorBase {
// (undocumented)
readonly errorType: DriverErrorType.genericError | DriverErrorType.fileNotFoundOrAccessDeniedError | DriverErrorType.offlineError | DriverErrorType.unsupportedClientProtocolVersion | DriverErrorType.writeError | DriverErrorType.fetchFailure | DriverErrorType.incorrectServerResponse | DriverErrorType.fileOverwrittenInStorage;
readonly errorType: DriverErrorType.genericError | DriverErrorType.fileNotFoundOrAccessDeniedError | DriverErrorType.offlineError | DriverErrorType.unsupportedClientProtocolVersion | DriverErrorType.writeError | DriverErrorType.fetchFailure | DriverErrorType.incorrectServerResponse | DriverErrorType.fileOverwrittenInStorage | DriverErrorType.usageError;
// (undocumented)
readonly statusCode?: number;
}

Просмотреть файл

@ -132,14 +132,14 @@ export function createGenericNetworkError(message: string, retryInfo: {
export const createWriteError: (message: string, props: DriverErrorTelemetryProps) => NonRetryableError<DriverErrorType.writeError>;
// @public (undocumented)
export class DeltaStreamConnectionForbiddenError extends LoggingError implements IFluidErrorBase {
export class DeltaStreamConnectionForbiddenError extends LoggingError implements IDriverErrorBase, IFluidErrorBase {
constructor(message: string, props: DriverErrorTelemetryProps);
// (undocumented)
readonly canRetry = false;
// (undocumented)
static readonly errorType: string;
static readonly errorType = DriverErrorType.deltaStreamConnectionForbidden;
// (undocumented)
readonly errorType: string;
readonly errorType = DriverErrorType.deltaStreamConnectionForbidden;
}
// @public (undocumented)
@ -430,10 +430,12 @@ export class ThrottlingError extends LoggingError implements IThrottlingWarning,
}
// @public
export class UsageError extends LoggingError implements IFluidErrorBase {
export class UsageError extends LoggingError implements IDriverErrorBase, IFluidErrorBase {
constructor(message: string);
// (undocumented)
readonly errorType = "usageError";
readonly canRetry = false;
// (undocumented)
readonly errorType = DriverErrorType.usageError;
}
// @public

Просмотреть файл

@ -32,7 +32,7 @@ export enum ContainerErrorType {
/**
* Error indicating an API is being used improperly resulting in an invalid operation.
*/
usageError = "usageError",
usageError = "usageError",
/**
* Error indicating an client session has expired. Currently this only happens when GC is allowed on a document and
@ -97,7 +97,7 @@ export interface IGenericError extends IErrorBase {
/**
* Error indicating an API is being used improperly resulting in an invalid operation.
*/
export interface IUsageError extends IErrorBase {
export interface IUsageError extends IErrorBase {
readonly errorType: ContainerErrorType.usageError;
}

Просмотреть файл

@ -71,18 +71,24 @@ export enum DriverErrorType {
* and storage / driver / loader detects such mismatch.
* When it's hit, client needs to forget all the knowlege about this file and start over.
*/
fileOverwrittenInStorage = "fileOverwrittenInStorage",
fileOverwrittenInStorage = "fileOverwrittenInStorage",
/**
* The document is read-only and delta stream connection is forbidden.
*/
deltaStreamConnectionForbidden = "deltaStreamConnectionForbidden",
/**
* The document is read-only and delta stream connection is forbidden.
*/
deltaStreamConnectionForbidden = "deltaStreamConnectionForbidden",
/**
* The location of file/container can change on server. So if the file location moves and we try to access the old
* location, then this error is thrown to let the client know about the new location info.
*/
locationRedirection = "locationRedirection",
/**
* Error indicating an API is being used improperly resulting in an invalid operation.
* ! Should match the value of ContainerErrorType.usageError
*/
usageError = "usageError",
}
/**
@ -136,7 +142,8 @@ export interface IDriverBasicError extends IDriverErrorBase {
| DriverErrorType.writeError
| DriverErrorType.fetchFailure
| DriverErrorType.incorrectServerResponse
| DriverErrorType.fileOverwrittenInStorage;
| DriverErrorType.fileOverwrittenInStorage
| DriverErrorType.usageError;
readonly statusCode?: number;
}

Просмотреть файл

@ -58,6 +58,7 @@
"version": "2.0.0",
"broken": {
"TypeAliasDeclaration_OdspError": {
"backCompat": false,
"forwardCompat": false
},
"InterfaceDeclaration_IOdspError": {

Просмотреть файл

@ -397,6 +397,7 @@ declare function get_current_TypeAliasDeclaration_OdspError():
declare function use_old_TypeAliasDeclaration_OdspError(
use: TypeOnly<old.OdspError>);
use_old_TypeAliasDeclaration_OdspError(
// @ts-expect-error compatibility expected to be broken
get_current_TypeAliasDeclaration_OdspError());
/*

Просмотреть файл

@ -5,7 +5,7 @@
import fs from "fs";
import { strict as assert } from "assert";
import { IStream } from "@fluidframework/driver-definitions";
import { DriverErrorType, IStream } from "@fluidframework/driver-definitions";
import { IOdspResolvedUrl } from "@fluidframework/odsp-driver-definitions";
import {
IClient,
@ -49,7 +49,7 @@ describe("Local Odsp driver", () => {
);
async function assertThrowsUsageError(fn: () => Promise<any>) {
await assert.rejects(fn, (e) => e.errorType === "usageError");
await assert.rejects(fn, (e) => e.errorType === DriverErrorType.usageError);
}
describe("Local Odsp document service factory", () => {

Просмотреть файл

@ -87,6 +87,10 @@
},
"typeValidation": {
"version": "2.0.0",
"broken": {}
"broken": {
"ClassDeclaration_UsageError": {
"forwardCompat": false
}
}
}
}

Просмотреть файл

@ -8,6 +8,7 @@ import {
IGenericError,
IErrorBase,
IThrottlingWarning,
IUsageError,
} from "@fluidframework/container-definitions";
import {
LoggingError,
@ -72,9 +73,8 @@ export class ThrottlingWarning extends LoggingError implements IThrottlingWarnin
}
/** Error indicating an API is being used improperly resulting in an invalid operation. */
export class UsageError extends LoggingError implements IFluidErrorBase {
// TODO: implement IUsageError once available
readonly errorType = "usageError";
export class UsageError extends LoggingError implements IUsageError, IFluidErrorBase {
readonly errorType = ContainerErrorType.usageError;
constructor(
message: string,

Просмотреть файл

@ -167,6 +167,7 @@ declare function get_old_ClassDeclaration_UsageError():
declare function use_current_ClassDeclaration_UsageError(
use: TypeOnly<current.UsageError>);
use_current_ClassDeclaration_UsageError(
// @ts-expect-error compatibility expected to be broken
get_old_ClassDeclaration_UsageError());
/*

Просмотреть файл

@ -107,6 +107,21 @@
"RemovedClassDeclaration_SummaryTreeAssembler": {
"forwardCompat": false,
"backCompat": false
},
"ClassDeclaration_ThrottlingError": {
"backCompat": false
},
"ClassDeclaration_AuthorizationError": {
"backCompat": false
},
"ClassDeclaration_GenericNetworkError": {
"backCompat": false
},
"ClassDeclaration_DeltaStreamConnectionForbiddenError": {
"forwardCompat": false
},
"ClassDeclaration_UsageError": {
"forwardCompat": false
}
}
}

Просмотреть файл

@ -3,11 +3,13 @@
* Licensed under the MIT License.
*/
import { DriverErrorType, IDriverErrorBase } from "@fluidframework/driver-definitions";
import { IFluidErrorBase, LoggingError } from "@fluidframework/telemetry-utils";
/** Error indicating an API is being used improperly resulting in an invalid operation. */
export class UsageError extends LoggingError implements IFluidErrorBase {
readonly errorType = "usageError";
export class UsageError extends LoggingError implements IDriverErrorBase, IFluidErrorBase {
readonly errorType = DriverErrorType.usageError;
readonly canRetry = false;
constructor(
message: string,

Просмотреть файл

@ -61,16 +61,9 @@ export class GenericNetworkError extends LoggingError implements IDriverErrorBas
}
}
// Todo GH #6214: Remove after next drive def bump. This is necessary as there is no
// compatible way to augment an enum, as it can't be optional. So for now
// we need to duplicate the value here. We likely need to rethink our
// DriverErrorType strategy so that it supports extension with optional
// value.
const deltaStreamConnectionForbiddenStr = "deltaStreamConnectionForbidden";
export class DeltaStreamConnectionForbiddenError extends LoggingError implements IFluidErrorBase {
static readonly errorType: string =
DriverErrorType[deltaStreamConnectionForbiddenStr] ?? deltaStreamConnectionForbiddenStr;
readonly errorType: string = DeltaStreamConnectionForbiddenError.errorType;
export class DeltaStreamConnectionForbiddenError extends LoggingError implements IDriverErrorBase, IFluidErrorBase {
static readonly errorType = DriverErrorType.deltaStreamConnectionForbidden;
readonly errorType = DeltaStreamConnectionForbiddenError.errorType;
readonly canRetry = false;
constructor(message: string, props: DriverErrorTelemetryProps) {

Просмотреть файл

@ -35,6 +35,7 @@ declare function get_current_ClassDeclaration_AuthorizationError():
declare function use_old_ClassDeclaration_AuthorizationError(
use: TypeOnly<old.AuthorizationError>);
use_old_ClassDeclaration_AuthorizationError(
// @ts-expect-error compatibility expected to be broken
get_current_ClassDeclaration_AuthorizationError());
/*
@ -287,6 +288,7 @@ declare function get_old_ClassDeclaration_DeltaStreamConnectionForbiddenError():
declare function use_current_ClassDeclaration_DeltaStreamConnectionForbiddenError(
use: TypeOnly<current.DeltaStreamConnectionForbiddenError>);
use_current_ClassDeclaration_DeltaStreamConnectionForbiddenError(
// @ts-expect-error compatibility expected to be broken
get_old_ClassDeclaration_DeltaStreamConnectionForbiddenError());
/*
@ -443,6 +445,7 @@ declare function get_current_ClassDeclaration_GenericNetworkError():
declare function use_old_ClassDeclaration_GenericNetworkError(
use: TypeOnly<old.GenericNetworkError>);
use_old_ClassDeclaration_GenericNetworkError(
// @ts-expect-error compatibility expected to be broken
get_current_ClassDeclaration_GenericNetworkError());
/*
@ -1211,6 +1214,7 @@ declare function get_current_ClassDeclaration_ThrottlingError():
declare function use_old_ClassDeclaration_ThrottlingError(
use: TypeOnly<old.ThrottlingError>);
use_old_ClassDeclaration_ThrottlingError(
// @ts-expect-error compatibility expected to be broken
get_current_ClassDeclaration_ThrottlingError());
/*
@ -1223,6 +1227,7 @@ declare function get_old_ClassDeclaration_UsageError():
declare function use_current_ClassDeclaration_UsageError(
use: TypeOnly<current.UsageError>);
use_current_ClassDeclaration_UsageError(
// @ts-expect-error compatibility expected to be broken
get_old_ClassDeclaration_UsageError());
/*