Skip to content

Commit

Permalink
Use idb 7 for products using shared idb library (#6154)
Browse files Browse the repository at this point in the history
  • Loading branch information
hsubox76 authored May 4, 2022
1 parent 5c92d9c commit 9c5c9c3
Showing 16 changed files with 112 additions and 290 deletions.
8 changes: 8 additions & 0 deletions .changeset/four-steaks-relate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@firebase/util': minor
'@firebase/app': patch
'@firebase/installations': patch
'@firebase/messaging': patch
---

Replace stopgap firebase/util IndexedDB methods with `idb` library.
29 changes: 0 additions & 29 deletions common/api-review/util.api.md
Original file line number Diff line number Diff line change
@@ -87,25 +87,6 @@ export function createMockUserToken(token: EmulatorMockTokenOptions, projectId?:
// @public
export function createSubscribe<T>(executor: Executor<T>, onNoObservers?: Executor<T>): Subscribe<T>;

// Warning: (ae-internal-missing-underscore) The name "DBWrapper" should be prefixed with an underscore because the declaration is marked as @internal
//
// @internal (undocumented)
export class DBWrapper {
constructor(_db: IDBDatabase);
// (undocumented)
close(): void;
// Warning: (ae-forgotten-export) The symbol "ObjectStoreWrapper" needs to be exported by the entry point index.d.ts
//
// (undocumented)
createObjectStore(storeName: string, options?: IDBObjectStoreParameters): ObjectStoreWrapper;
// (undocumented)
objectStoreNames: DOMStringList;
// Warning: (ae-forgotten-export) The symbol "TransactionWrapper" needs to be exported by the entry point index.d.ts
//
// (undocumented)
transaction(storeNames: string[] | string, mode?: IDBTransactionMode): TransactionWrapper;
}

// Warning: (ae-forgotten-export) The symbol "DecodedToken" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "decode" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -141,11 +122,6 @@ export class Deferred<R> {
wrapCallback(callback?: (error?: unknown, value?: unknown) => void): (error: unknown, value?: unknown) => void;
}

// Warning: (ae-internal-missing-underscore) The name "deleteDB" should be prefixed with an underscore because the declaration is marked as @internal
//
// @internal (undocumented)
export function deleteDB(dbName: string): Promise<void>;

// Warning: (ae-forgotten-export) The symbol "FirebaseIdToken" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "EmulatorMockTokenOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -360,11 +336,6 @@ export interface Observer<T> {
next: NextFn<T>;
}

// Warning: (ae-internal-missing-underscore) The name "openDB" should be prefixed with an underscore because the declaration is marked as @internal
//
// @internal (undocumented)
export function openDB(dbName: string, dbVersion: number, upgradeCallback: (db: DBWrapper, oldVersion: number, newVersion: number | null, transaction: TransactionWrapper) => void): Promise<DBWrapper>;

// Warning: (ae-missing-release-tag) "ordinal" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
5 changes: 3 additions & 2 deletions packages/app/package.json
Original file line number Diff line number Diff line change
@@ -40,12 +40,13 @@
"@firebase/util": "1.5.2",
"@firebase/logger": "0.3.2",
"@firebase/component": "0.5.13",
"idb": "7.0.1",
"tslib": "^2.1.0"
},
"license": "Apache-2.0",
"devDependencies": {
"rollup": "2.57.0",
"@rollup/plugin-json": "4.1.0",
"rollup": "2.57.0",
"rollup-plugin-replace": "2.2.0",
"rollup-plugin-typescript2": "0.30.0",
"typescript": "4.2.2"
@@ -65,4 +66,4 @@
],
"reportDir": "./coverage/node"
}
}
}
36 changes: 23 additions & 13 deletions packages/app/src/indexeddb.ts
Original file line number Diff line number Diff line change
@@ -15,26 +15,36 @@
* limitations under the License.
*/

import { DBWrapper, openDB } from '@firebase/util';
import { DBSchema, openDB, IDBPDatabase } from 'idb';
import { AppError, ERROR_FACTORY } from './errors';
import { FirebaseApp } from './public-types';
import { HeartbeatsInIndexedDB } from './types';

const DB_NAME = 'firebase-heartbeat-database';
const DB_VERSION = 1;
const STORE_NAME = 'firebase-heartbeat-store';

let dbPromise: Promise<DBWrapper> | null = null;
function getDbPromise(): Promise<DBWrapper> {
interface AppDB extends DBSchema {
'firebase-heartbeat-store': {
key: string;
value: HeartbeatsInIndexedDB;
};
}

let dbPromise: Promise<IDBPDatabase<AppDB>> | null = null;
function getDbPromise(): Promise<IDBPDatabase<AppDB>> {
if (!dbPromise) {
dbPromise = openDB(DB_NAME, DB_VERSION, (db, oldVersion) => {
// We don't use 'break' in this switch statement, the fall-through
// behavior is what we want, because if there are multiple versions between
// the old version and the current version, we want ALL the migrations
// that correspond to those versions to run, not only the last one.
// eslint-disable-next-line default-case
switch (oldVersion) {
case 0:
db.createObjectStore(STORE_NAME);
dbPromise = openDB<AppDB>(DB_NAME, DB_VERSION, {
upgrade: (db, oldVersion) => {
// We don't use 'break' in this switch statement, the fall-through
// behavior is what we want, because if there are multiple versions between
// the old version and the current version, we want ALL the migrations
// that correspond to those versions to run, not only the last one.
// eslint-disable-next-line default-case
switch (oldVersion) {
case 0:
db.createObjectStore(STORE_NAME);
}
}
}).catch(e => {
throw ERROR_FACTORY.create(AppError.STORAGE_OPEN, {
@@ -70,7 +80,7 @@ export async function writeHeartbeatsToIndexedDB(
const tx = db.transaction(STORE_NAME, 'readwrite');
const objectStore = tx.objectStore(STORE_NAME);
await objectStore.put(heartbeatObject, computeKey(app));
return tx.complete;
return tx.done;
} catch (e) {
throw ERROR_FACTORY.create(AppError.STORAGE_WRITE, {
originalErrorMessage: e.message
1 change: 1 addition & 0 deletions packages/installations/package.json
Original file line number Diff line number Diff line change
@@ -64,6 +64,7 @@
"dependencies": {
"@firebase/util": "1.5.2",
"@firebase/component": "0.5.13",
"idb": "7.0.1",
"tslib": "^2.1.0"
}
}
41 changes: 25 additions & 16 deletions packages/installations/src/helpers/idb-manager.ts
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@
* limitations under the License.
*/

import { DBWrapper, openDB } from '@firebase/util';
import { DBSchema, IDBPDatabase, openDB } from 'idb';
import { AppConfig } from '../interfaces/installation-impl';
import { InstallationEntry } from '../interfaces/installation-entry';
import { getKey } from '../util/get-key';
@@ -25,18 +25,27 @@ const DATABASE_NAME = 'firebase-installations-database';
const DATABASE_VERSION = 1;
const OBJECT_STORE_NAME = 'firebase-installations-store';

let dbPromise: Promise<DBWrapper> | null = null;
function getDbPromise(): Promise<DBWrapper> {
interface InstallationsDB extends DBSchema {
'firebase-installations-store': {
key: string;
value: InstallationEntry | undefined;
};
}

let dbPromise: Promise<IDBPDatabase<InstallationsDB>> | null = null;
function getDbPromise(): Promise<IDBPDatabase<InstallationsDB>> {
if (!dbPromise) {
dbPromise = openDB(DATABASE_NAME, DATABASE_VERSION, (db, oldVersion) => {
// We don't use 'break' in this switch statement, the fall-through
// behavior is what we want, because if there are multiple versions between
// the old version and the current version, we want ALL the migrations
// that correspond to those versions to run, not only the last one.
// eslint-disable-next-line default-case
switch (oldVersion) {
case 0:
db.createObjectStore(OBJECT_STORE_NAME);
dbPromise = openDB(DATABASE_NAME, DATABASE_VERSION, {
upgrade: (db, oldVersion) => {
// We don't use 'break' in this switch statement, the fall-through
// behavior is what we want, because if there are multiple versions between
// the old version and the current version, we want ALL the migrations
// that correspond to those versions to run, not only the last one.
// eslint-disable-next-line default-case
switch (oldVersion) {
case 0:
db.createObjectStore(OBJECT_STORE_NAME);
}
}
});
}
@@ -66,7 +75,7 @@ export async function set<ValueType extends InstallationEntry>(
const objectStore = tx.objectStore(OBJECT_STORE_NAME);
const oldValue = (await objectStore.get(key)) as InstallationEntry;
await objectStore.put(value, key);
await tx.complete;
await tx.done;

if (!oldValue || oldValue.fid !== value.fid) {
fidChanged(appConfig, value.fid);
@@ -81,7 +90,7 @@ export async function remove(appConfig: AppConfig): Promise<void> {
const db = await getDbPromise();
const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');
await tx.objectStore(OBJECT_STORE_NAME).delete(key);
await tx.complete;
await tx.done;
}

/**
@@ -108,7 +117,7 @@ export async function update<ValueType extends InstallationEntry | undefined>(
} else {
await store.put(newValue, key);
}
await tx.complete;
await tx.done;

if (newValue && (!oldValue || oldValue.fid !== newValue.fid)) {
fidChanged(appConfig, newValue.fid);
@@ -121,5 +130,5 @@ export async function clear(): Promise<void> {
const db = await getDbPromise();
const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');
await tx.objectStore(OBJECT_STORE_NAME).clear();
await tx.complete;
await tx.done;
}
1 change: 1 addition & 0 deletions packages/messaging/package.json
Original file line number Diff line number Diff line change
@@ -56,6 +56,7 @@
"@firebase/messaging-interop-types": "0.1.0",
"@firebase/util": "1.5.2",
"@firebase/component": "0.5.13",
"idb": "7.0.1",
"tslib": "^2.1.0"
},
"devDependencies": {
24 changes: 13 additions & 11 deletions packages/messaging/src/helpers/migrate-old-database.test.ts
Original file line number Diff line number Diff line change
@@ -28,7 +28,7 @@ import { FakePushSubscription } from '../testing/fakes/service-worker';
import { base64ToArray } from './array-base64-translator';
import { expect } from 'chai';
import { getFakeTokenDetails } from '../testing/fakes/token-details';
import { openDB } from '@firebase/util';
import { openDB } from 'idb';

describe('migrateOldDb', () => {
it("does nothing if old DB didn't exist", async () => {
@@ -179,22 +179,24 @@ describe('migrateOldDb', () => {
});

async function put(version: number, value: object): Promise<void> {
const db = await openDB('fcm_token_details_db', version, (db, oldVersion) => {
if (oldVersion === 0) {
const objectStore = db.createObjectStore('fcm_token_object_Store', {
keyPath: 'swScope'
});
objectStore.createIndex('fcmSenderId', 'fcmSenderId', {
unique: false
});
objectStore.createIndex('fcmToken', 'fcmToken', { unique: true });
const db = await openDB('fcm_token_details_db', version, {
upgrade: (db, oldVersion) => {
if (oldVersion === 0) {
const objectStore = db.createObjectStore('fcm_token_object_Store', {
keyPath: 'swScope'
});
objectStore.createIndex('fcmSenderId', 'fcmSenderId', {
unique: false
});
objectStore.createIndex('fcmToken', 'fcmToken', { unique: true });
}
}
});

try {
const tx = db.transaction('fcm_token_object_Store', 'readwrite');
await tx.objectStore('fcm_token_object_Store').put(value);
await tx.complete;
await tx.done;
} finally {
db.close();
}
10 changes: 4 additions & 6 deletions packages/messaging/src/helpers/migrate-old-database.ts
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@
* limitations under the License.
*/

import { deleteDB, openDB } from '@firebase/util';
import { deleteDB, openDB } from 'idb';

import { TokenDetails } from '../interfaces/token-details';
import { arrayToBase64 } from './array-base64-translator';
@@ -88,10 +88,8 @@ export async function migrateOldDatabase(

let tokenDetails: TokenDetails | null = null;

const db = await openDB(
OLD_DB_NAME,
OLD_DB_VERSION,
async (db, oldVersion, newVersion, upgradeTransaction) => {
const db = await openDB(OLD_DB_NAME, OLD_DB_VERSION, {
upgrade: async (db, oldVersion, newVersion, upgradeTransaction) => {
if (oldVersion < 2) {
// Database too old, skip migration.
return;
@@ -162,7 +160,7 @@ export async function migrateOldDatabase(
};
}
}
);
});
db.close();

// Delete all old databases.
25 changes: 15 additions & 10 deletions packages/messaging/src/internals/idb-manager.ts
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@
* limitations under the License.
*/

import { DBWrapper, deleteDB, openDB } from '@firebase/util';
import { DBSchema, IDBPDatabase, deleteDB, openDB } from 'idb';

import { FirebaseInternalDependencies } from '../interfaces/internal-dependencies';
import { TokenDetails } from '../interfaces/token-details';
@@ -26,13 +26,18 @@ export const DATABASE_NAME = 'firebase-messaging-database';
const DATABASE_VERSION = 1;
const OBJECT_STORE_NAME = 'firebase-messaging-store';

let dbPromise: Promise<DBWrapper> | null = null;
function getDbPromise(): Promise<DBWrapper> {
interface MessagingDB extends DBSchema {
'firebase-messaging-store': {
key: string;
value: TokenDetails;
};
}

let dbPromise: Promise<IDBPDatabase<MessagingDB>> | null = null;
function getDbPromise(): Promise<IDBPDatabase<MessagingDB>> {
if (!dbPromise) {
dbPromise = openDB(
DATABASE_NAME,
DATABASE_VERSION,
(upgradeDb, oldVersion) => {
dbPromise = openDB(DATABASE_NAME, DATABASE_VERSION, {
upgrade: (upgradeDb, oldVersion) => {
// We don't use 'break' in this switch statement, the fall-through behavior is what we want,
// because if there are multiple versions between the old version and the current version, we
// want ALL the migrations that correspond to those versions to run, not only the last one.
@@ -42,7 +47,7 @@ function getDbPromise(): Promise<DBWrapper> {
upgradeDb.createObjectStore(OBJECT_STORE_NAME);
}
}
);
});
}
return dbPromise;
}
@@ -81,7 +86,7 @@ export async function dbSet(
const db = await getDbPromise();
const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');
await tx.objectStore(OBJECT_STORE_NAME).put(tokenDetails, key);
await tx.complete;
await tx.done;
return tokenDetails;
}

@@ -93,7 +98,7 @@ export async function dbRemove(
const db = await getDbPromise();
const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');
await tx.objectStore(OBJECT_STORE_NAME).delete(key);
await tx.complete;
await tx.done;
}

/** Deletes the DB. Useful for tests. */
Loading

0 comments on commit 9c5c9c3

Please sign in to comment.