Skip to content

Commit

Permalink
Warn instead of throw for idb errors in core app (#6480)
Browse files Browse the repository at this point in the history
  • Loading branch information
hsubox76 authored Aug 2, 2022
1 parent 6a17eb6 commit 82a6add
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 23 deletions.
5 changes: 5 additions & 0 deletions .changeset/calm-pugs-leave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@firebase/app': patch
---

Prevent core app from throwing if IndexedDB heartbeat functions throw.
32 changes: 16 additions & 16 deletions packages/app/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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> = {
Expand All @@ -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 {
Expand All @@ -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>(
Expand Down
77 changes: 77 additions & 0 deletions packages/app/src/indexeddb.test.ts
Original file line number Diff line number Diff line change
@@ -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));
}
});
});
26 changes: 19 additions & 7 deletions packages/app/src/indexeddb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
});
});
Expand All @@ -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);
}
}
}

Expand All @@ -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);
}
}
}

Expand Down

0 comments on commit 82a6add

Please sign in to comment.