Warn instead of throw for idb errors in core app (#6480)

This commit is contained in:
Christina Holland
2022-08-02 14:41:50 -07:00
committed by GitHub
parent 1df9db9510
commit 7471fbb251
4 changed files with 117 additions and 23 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@firebase/app': patch
---
Prevent core app from throwing if IndexedDB heartbeat functions throw.
+16 -16
View File
@@ -24,10 +24,10 @@ export const enum AppError {
APP_DELETED = 'app-deleted',
INVALID_APP_ARGUMENT = 'invalid-app-argument',
INVALID_LOG_ARGUMENT = 'invalid-log-argument',
STORAGE_OPEN = 'storage-open',
STORAGE_GET = 'storage-get',
STORAGE_WRITE = 'storage-set',
STORAGE_DELETE = 'storage-delete'
IDB_OPEN = 'idb-open',
IDB_GET = 'idb-get',
IDB_WRITE = 'idb-set',
IDB_DELETE = 'idb-delete'
}
const ERRORS: ErrorMap<AppError> = {
@@ -43,14 +43,14 @@ const ERRORS: ErrorMap<AppError> = {
'Firebase App instance.',
[AppError.INVALID_LOG_ARGUMENT]:
'First argument to `onLog` must be null or a function.',
[AppError.STORAGE_OPEN]:
'Error thrown when opening storage. Original error: {$originalErrorMessage}.',
[AppError.STORAGE_GET]:
'Error thrown when reading from storage. Original error: {$originalErrorMessage}.',
[AppError.STORAGE_WRITE]:
'Error thrown when writing to storage. Original error: {$originalErrorMessage}.',
[AppError.STORAGE_DELETE]:
'Error thrown when deleting from storage. Original error: {$originalErrorMessage}.'
[AppError.IDB_OPEN]:
'Error thrown when opening IndexedDB. Original error: {$originalErrorMessage}.',
[AppError.IDB_GET]:
'Error thrown when reading from IndexedDB. Original error: {$originalErrorMessage}.',
[AppError.IDB_WRITE]:
'Error thrown when writing to IndexedDB. Original error: {$originalErrorMessage}.',
[AppError.IDB_DELETE]:
'Error thrown when deleting from IndexedDB. Original error: {$originalErrorMessage}.'
};
interface ErrorParams {
@@ -59,10 +59,10 @@ interface ErrorParams {
[AppError.DUPLICATE_APP]: { appName: string };
[AppError.APP_DELETED]: { appName: string };
[AppError.INVALID_APP_ARGUMENT]: { appName: string };
[AppError.STORAGE_OPEN]: { originalErrorMessage?: string };
[AppError.STORAGE_GET]: { originalErrorMessage?: string };
[AppError.STORAGE_WRITE]: { originalErrorMessage?: string };
[AppError.STORAGE_DELETE]: { originalErrorMessage?: string };
[AppError.IDB_OPEN]: { originalErrorMessage?: string };
[AppError.IDB_GET]: { originalErrorMessage?: string };
[AppError.IDB_WRITE]: { originalErrorMessage?: string };
[AppError.IDB_DELETE]: { originalErrorMessage?: string };
}
export const ERROR_FACTORY = new ErrorFactory<AppError, ErrorParams>(
+77
View File
@@ -0,0 +1,77 @@
/**
* @license
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { expect } from 'chai';
import '../test/setup';
import { match, stub } from 'sinon';
import {
readHeartbeatsFromIndexedDB,
writeHeartbeatsToIndexedDB
} from './indexeddb';
import { FirebaseApp } from './public-types';
import { AppError } from './errors';
import { HeartbeatsInIndexedDB } from './types';
/**
* Mostly testing failure cases. heartbeatService.test.ts tests read-write
* more extensively.
*/
describe('IndexedDB functions', () => {
it('readHeartbeatsFromIndexedDB warns if IndexedDB.open() throws', async () => {
const warnStub = stub(console, 'warn');
if (typeof window !== 'undefined') {
// Ensure that indexedDB.open() fails in browser. It will always fail in Node.
stub(window.indexedDB, 'open').throws(new Error('abcd'));
await readHeartbeatsFromIndexedDB({
name: 'testname',
options: { appId: 'test-app-id' }
} as FirebaseApp);
expect(warnStub).to.be.calledWith(match.any, match(AppError.IDB_GET));
} else {
await readHeartbeatsFromIndexedDB({
name: 'testname',
options: { appId: 'test-app-id' }
} as FirebaseApp);
expect(warnStub).to.be.calledWith(match.any, match(AppError.IDB_GET));
}
});
it('writeHeartbeatsToIndexedDB warns if IndexedDB.open() throws', async () => {
const warnStub = stub(console, 'warn');
if (typeof window !== 'undefined') {
// Ensure that indexedDB.open() fails in browser. It will always fail in Node.
stub(window.indexedDB, 'open').throws(new Error('abcd'));
await writeHeartbeatsToIndexedDB(
{
name: 'testname',
options: { appId: 'test-app-id' }
} as FirebaseApp,
{} as HeartbeatsInIndexedDB
);
expect(warnStub).to.be.calledWith(match.any, match(AppError.IDB_WRITE));
} else {
await writeHeartbeatsToIndexedDB(
{
name: 'testname',
options: { appId: 'test-app-id' }
} as FirebaseApp,
{} as HeartbeatsInIndexedDB
);
expect(warnStub).to.be.calledWith(match.any, match(AppError.IDB_WRITE));
}
});
});
+19 -7
View File
@@ -15,10 +15,12 @@
* limitations under the License.
*/
import { FirebaseError } from '@firebase/util';
import { DBSchema, openDB, IDBPDatabase } from 'idb';
import { AppError, ERROR_FACTORY } from './errors';
import { FirebaseApp } from './public-types';
import { HeartbeatsInIndexedDB } from './types';
import { logger } from './logger';
const DB_NAME = 'firebase-heartbeat-database';
const DB_VERSION = 1;
@@ -47,7 +49,7 @@ function getDbPromise(): Promise<IDBPDatabase<AppDB>> {
}
}
}).catch(e => {
throw ERROR_FACTORY.create(AppError.STORAGE_OPEN, {
throw ERROR_FACTORY.create(AppError.IDB_OPEN, {
originalErrorMessage: e.message
});
});
@@ -65,9 +67,14 @@ export async function readHeartbeatsFromIndexedDB(
.objectStore(STORE_NAME)
.get(computeKey(app)) as Promise<HeartbeatsInIndexedDB | undefined>;
} catch (e) {
throw ERROR_FACTORY.create(AppError.STORAGE_GET, {
originalErrorMessage: (e as Error)?.message
});
if (e instanceof FirebaseError) {
logger.warn(e.message);
} else {
const idbGetError = ERROR_FACTORY.create(AppError.IDB_GET, {
originalErrorMessage: (e as Error)?.message
});
logger.warn(idbGetError.message);
}
}
}
@@ -82,9 +89,14 @@ export async function writeHeartbeatsToIndexedDB(
await objectStore.put(heartbeatObject, computeKey(app));
return tx.done;
} catch (e) {
throw ERROR_FACTORY.create(AppError.STORAGE_WRITE, {
originalErrorMessage: (e as Error)?.message
});
if (e instanceof FirebaseError) {
logger.warn(e.message);
} else {
const idbGetError = ERROR_FACTORY.create(AppError.IDB_WRITE, {
originalErrorMessage: (e as Error)?.message
});
logger.warn(idbGetError.message);
}
}
}