From f63d34d2c1c3c40e167631906eda7b3323614b28 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 26 Feb 2025 13:34:27 -0500 Subject: [PATCH 1/4] Add API for partial errors --- packages/data-connect/src/api/index.ts | 8 ++++ packages/data-connect/src/core/error.ts | 62 ++++++++++++++++++++++--- 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/packages/data-connect/src/api/index.ts b/packages/data-connect/src/api/index.ts index 885dac5a923..71bd95557dc 100644 --- a/packages/data-connect/src/api/index.ts +++ b/packages/data-connect/src/api/index.ts @@ -22,3 +22,11 @@ export * from './Mutation'; export * from './query'; export { setLogLevel } from '../logger'; export { validateArgs } from '../util/validateArgs'; + +export { DataConnectError, DataConnectOperationError } from '../core/error'; + +export type { + DataConnectErrorCode, + DataConnectOperationResponse, + DataConnectOperationErrorInfo +} from '../core/error'; diff --git a/packages/data-connect/src/core/error.ts b/packages/data-connect/src/core/error.ts index f0beb128afa..6aed2d4bb0f 100644 --- a/packages/data-connect/src/core/error.ts +++ b/packages/data-connect/src/core/error.ts @@ -40,8 +40,8 @@ export const Code = { /** An error returned by a DataConnect operation. */ export class DataConnectError extends FirebaseError { - /** The stack of the error. */ - readonly stack?: string; + /** The custom name for DataConnectError. */ + readonly name: string = 'DataConnectError'; /** @hideconstructor */ constructor( @@ -56,9 +56,59 @@ export class DataConnectError extends FirebaseError { ) { super(code, message); - // HACK: We write a toString property directly because Error is not a real - // class and so inheritance does not work correctly. We could alternatively - // do the same "back-door inheritance" trick that FirebaseError does. - this.toString = () => `${this.name}: [code=${this.code}]: ${this.message}`; + // Ensure the instanceof operator works as expected on subclasses of Error. + // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#custom_error_types + // and https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget + Object.setPrototypeOf(this, new.target.prototype); } + + toString(): string { + return `${this.name}[code=${this.code}]: ${this.message}`; + } +} + +/** An error returned by a DataConnect operation. */ +export class DataConnectOperationError extends DataConnectError { + /** The custom name for DataConnectOperationError. */ + readonly name: string = 'DataConnectOperationError'; + + /** The response received from the backend. */ + readonly response: DataConnectOperationResponse; + + /** @hideconstructor */ + constructor(message: string, response: DataConnectOperationResponse) { + super('partial-error', message); + this.response = response; + + // Ensure the instanceof operator works as expected on subclasses of Error. + // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#custom_error_types + // and https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget + Object.setPrototypeOf(this, new.target.prototype); + } +} + +export interface DataConnectOperationResponse { + // The "data" provided by the backend in the response message. + // + // Will be `undefined` if no "data" was provided in the response message. + // Otherwise, will be `null` if `null` was explicitly specified as the "data" + // in the response message. Otherwise, will be the value of the "data" + // specified as the "data" in the response message + readonly data?: Record | null; + + // The list of errors provided by the backend in the response message. + readonly errors: DataConnectOperationErrorInfo[]; +} + +// Information about the error, as provided in the response from the backend. +// See https://spec.graphql.org/draft/#sec-Errors +export interface DataConnectOperationErrorInfo { + // The error message. + readonly message: string; + + // The path of the field in the response data to which this error relates. + // String values in this array refer to field names. Numeric values in this + // array always satisfy `Number.isInteger()` and refer to the index in an + // array. + readonly path: Array; } From 6032daff7f2a52af3cca234344a85555cfddb96b Mon Sep 17 00:00:00 2001 From: dconeybe Date: Wed, 26 Feb 2025 18:43:58 +0000 Subject: [PATCH 2/4] Update API reports --- common/api-review/data-connect.api.md | 38 +++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/common/api-review/data-connect.api.md b/common/api-review/data-connect.api.md index 1a698c229b4..833a988bfb9 100644 --- a/common/api-review/data-connect.api.md +++ b/common/api-review/data-connect.api.md @@ -52,6 +52,40 @@ export class DataConnect { setInitialized(): void; } +// @public +export class DataConnectError extends FirebaseError { + readonly code: DataConnectErrorCode; + readonly message: string; + readonly name: string; + // (undocumented) + toString(): string; +} + +// @public (undocumented) +export type DataConnectErrorCode = 'other' | 'already-initialized' | 'not-initialized' | 'not-supported' | 'invalid-argument' | 'partial-error' | 'unauthorized'; + +// @public +export class DataConnectOperationError extends DataConnectError { + readonly name: string; + readonly response: DataConnectOperationResponse; +} + +// @public (undocumented) +export interface DataConnectOperationErrorInfo { + // (undocumented) + readonly message: string; + // (undocumented) + readonly path: Array; +} + +// @public (undocumented) +export interface DataConnectOperationResponse { + // (undocumented) + readonly data?: Record | null; + // (undocumented) + readonly errors: DataConnectOperationErrorInfo[]; +} + // @public export interface DataConnectOptions extends ConnectorConfig { // (undocumented) @@ -67,7 +101,7 @@ export interface DataConnectResult extends OpResult { // @public export interface DataConnectSubscription { // (undocumented) - errCallback?: (e?: FirebaseError) => void; + errCallback?: (e?: DataConnectError) => void; // (undocumented) unsubscribe: () => void; // (undocumented) @@ -118,7 +152,7 @@ export interface MutationResult extends DataConnectResult void; // @public -export type OnErrorSubscription = (err?: FirebaseError) => void; +export type OnErrorSubscription = (err?: DataConnectError) => void; // @public export type OnResultSubscription = (res: QueryResult) => void; From c5d38292bf7ba4798aa3dd37ba7fa28c38825557 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 26 Feb 2025 13:58:53 -0500 Subject: [PATCH 3/4] src/core/error.ts: cleanups --- packages/data-connect/src/core/error.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/data-connect/src/core/error.ts b/packages/data-connect/src/core/error.ts index 6aed2d4bb0f..3c817f616c3 100644 --- a/packages/data-connect/src/core/error.ts +++ b/packages/data-connect/src/core/error.ts @@ -40,7 +40,7 @@ export const Code = { /** An error returned by a DataConnect operation. */ export class DataConnectError extends FirebaseError { - /** The custom name for DataConnectError. */ + /** @internal */ readonly name: string = 'DataConnectError'; /** @hideconstructor */ @@ -52,7 +52,7 @@ export class DataConnectError extends FirebaseError { /** * A custom error description. */ - readonly message: string + message: string ) { super(code, message); @@ -62,6 +62,7 @@ export class DataConnectError extends FirebaseError { Object.setPrototypeOf(this, new.target.prototype); } + /** @internal */ toString(): string { return `${this.name}[code=${this.code}]: ${this.message}`; } @@ -69,7 +70,7 @@ export class DataConnectError extends FirebaseError { /** An error returned by a DataConnect operation. */ export class DataConnectOperationError extends DataConnectError { - /** The custom name for DataConnectOperationError. */ + /** @internal */ readonly name: string = 'DataConnectOperationError'; /** The response received from the backend. */ From 8a33180826a6ac273ff19424a6c05a3c899173d8 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 26 Feb 2025 14:00:27 -0500 Subject: [PATCH 4/4] data-connect.api.md: updated --- common/api-review/data-connect.api.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/common/api-review/data-connect.api.md b/common/api-review/data-connect.api.md index 833a988bfb9..1b93be0e9c3 100644 --- a/common/api-review/data-connect.api.md +++ b/common/api-review/data-connect.api.md @@ -55,10 +55,6 @@ export class DataConnect { // @public export class DataConnectError extends FirebaseError { readonly code: DataConnectErrorCode; - readonly message: string; - readonly name: string; - // (undocumented) - toString(): string; } // @public (undocumented) @@ -66,7 +62,7 @@ export type DataConnectErrorCode = 'other' | 'already-initialized' | 'not-initia // @public export class DataConnectOperationError extends DataConnectError { - readonly name: string; + /* Excluded from this release type: name */ readonly response: DataConnectOperationResponse; }