Skip to content

Commit

Permalink
Add a stopgap idb replacement (#6061)
Browse files Browse the repository at this point in the history
  • Loading branch information
hsubox76 authored Mar 14, 2022
1 parent 927c1af commit 2d672ce
Show file tree
Hide file tree
Showing 16 changed files with 303 additions and 119 deletions.
9 changes: 9 additions & 0 deletions .changeset/empty-falcons-sit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@firebase/app': patch
'@firebase/installations': patch
'@firebase/installations-compat': patch
'@firebase/messaging': patch
'@firebase/util': minor
---

Remove idb dependency and replace with our own code.
1 change: 0 additions & 1 deletion packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
"@firebase/util": "1.4.3",
"@firebase/logger": "0.3.2",
"@firebase/component": "0.5.10",
"idb": "3.0.2",
"tslib": "^2.1.0"
},
"license": "Apache-2.0",
Expand Down
14 changes: 7 additions & 7 deletions packages/app/src/indexeddb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,26 @@
* limitations under the License.
*/

import { DB, openDb } from 'idb';
import { DBWrapper, openDB } from '@firebase/util';
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<DB> | null = null;
function getDbPromise(): Promise<DB> {
let dbPromise: Promise<DBWrapper> | null = null;
function getDbPromise(): Promise<DBWrapper> {
if (!dbPromise) {
dbPromise = openDb(DB_NAME, DB_VERSION, upgradeDB => {
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 (upgradeDB.oldVersion) {
switch (oldVersion) {
case 0:
upgradeDB.createObjectStore(STORE_NAME);
db.createObjectStore(STORE_NAME);
}
}).catch(e => {
throw ERROR_FACTORY.create(AppError.STORAGE_OPEN, {
Expand All @@ -53,7 +53,7 @@ export async function readHeartbeatsFromIndexedDB(
return db
.transaction(STORE_NAME)
.objectStore(STORE_NAME)
.get(computeKey(app));
.get(computeKey(app)) as Promise<HeartbeatsInIndexedDB | undefined>;
} catch (e) {
throw ERROR_FACTORY.create(AppError.STORAGE_GET, {
originalErrorMessage: e.message
Expand Down
1 change: 0 additions & 1 deletion packages/installations-compat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@
"@firebase/installations-types": "0.4.0",
"@firebase/util": "1.4.3",
"@firebase/component": "0.5.10",
"idb": "3.0.2",
"tslib": "^2.1.0"
}
}
1 change: 0 additions & 1 deletion packages/installations/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@
"dependencies": {
"@firebase/util": "1.4.3",
"@firebase/component": "0.5.10",
"idb": "3.0.2",
"tslib": "^2.1.0"
}
}
20 changes: 11 additions & 9 deletions packages/installations/src/helpers/idb-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* limitations under the License.
*/

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

let dbPromise: Promise<DB> | null = null;
function getDbPromise(): Promise<DB> {
let dbPromise: Promise<DBWrapper> | null = null;
function getDbPromise(): Promise<DBWrapper> {
if (!dbPromise) {
dbPromise = openDb(DATABASE_NAME, DATABASE_VERSION, upgradeDB => {
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 (upgradeDB.oldVersion) {
switch (oldVersion) {
case 0:
upgradeDB.createObjectStore(OBJECT_STORE_NAME);
db.createObjectStore(OBJECT_STORE_NAME);
}
});
}
Expand All @@ -52,7 +52,7 @@ export async function get(
return db
.transaction(OBJECT_STORE_NAME)
.objectStore(OBJECT_STORE_NAME)
.get(key);
.get(key) as Promise<InstallationEntry>;
}

/** Assigns or overwrites the record for the given key with the given value. */
Expand All @@ -64,7 +64,7 @@ export async function set<ValueType extends InstallationEntry>(
const db = await getDbPromise();
const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');
const objectStore = tx.objectStore(OBJECT_STORE_NAME);
const oldValue = await objectStore.get(key);
const oldValue = (await objectStore.get(key)) as InstallationEntry;
await objectStore.put(value, key);
await tx.complete;

Expand Down Expand Up @@ -98,7 +98,9 @@ export async function update<ValueType extends InstallationEntry | undefined>(
const db = await getDbPromise();
const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');
const store = tx.objectStore(OBJECT_STORE_NAME);
const oldValue: InstallationEntry | undefined = await store.get(key);
const oldValue: InstallationEntry | undefined = (await store.get(
key
)) as InstallationEntry;
const newValue = updateFn(oldValue);

if (newValue === undefined) {
Expand Down
1 change: 0 additions & 1 deletion packages/messaging/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
"@firebase/messaging-interop-types": "0.1.0",
"@firebase/util": "1.4.3",
"@firebase/component": "0.5.10",
"idb": "3.0.2",
"tslib": "^2.1.0"
},
"devDependencies": {
Expand Down
15 changes: 6 additions & 9 deletions packages/messaging/src/helpers/migrate-old-database.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 'idb';
import { openDB } from '@firebase/util';

describe('migrateOldDb', () => {
it("does nothing if old DB didn't exist", async () => {
Expand Down Expand Up @@ -179,14 +179,11 @@ describe('migrateOldDb', () => {
});

async function put(version: number, value: object): Promise<void> {
const db = await openDb('fcm_token_details_db', version, upgradeDb => {
if (upgradeDb.oldVersion === 0) {
const objectStore = upgradeDb.createObjectStore(
'fcm_token_object_Store',
{
keyPath: 'swScope'
}
);
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
});
Expand Down
138 changes: 71 additions & 67 deletions packages/messaging/src/helpers/migrate-old-database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* limitations under the License.
*/

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

import { TokenDetails } from '../interfaces/token-details';
import { arrayToBase64 } from './array-base64-translator';
Expand Down Expand Up @@ -88,83 +88,87 @@ export async function migrateOldDatabase(

let tokenDetails: TokenDetails | null = null;

const db = await openDb(OLD_DB_NAME, OLD_DB_VERSION, async db => {
if (db.oldVersion < 2) {
// Database too old, skip migration.
return;
}

if (!db.objectStoreNames.contains(OLD_OBJECT_STORE_NAME)) {
// Database did not exist. Nothing to do.
return;
}

const objectStore = db.transaction.objectStore(OLD_OBJECT_STORE_NAME);
const value = await objectStore.index('fcmSenderId').get(senderId);
await objectStore.clear();
const db = await openDB(
OLD_DB_NAME,
OLD_DB_VERSION,
async (db, oldVersion, newVersion, upgradeTransaction) => {
if (oldVersion < 2) {
// Database too old, skip migration.
return;
}

if (!value) {
// No entry in the database, nothing to migrate.
return;
}
if (!db.objectStoreNames.contains(OLD_OBJECT_STORE_NAME)) {
// Database did not exist. Nothing to do.
return;
}

if (db.oldVersion === 2) {
const oldDetails = value as V2TokenDetails;
const objectStore = upgradeTransaction.objectStore(OLD_OBJECT_STORE_NAME);
const value = await objectStore.index('fcmSenderId').get(senderId);
await objectStore.clear();

if (!oldDetails.auth || !oldDetails.p256dh || !oldDetails.endpoint) {
if (!value) {
// No entry in the database, nothing to migrate.
return;
}

tokenDetails = {
token: oldDetails.fcmToken,
createTime: oldDetails.createTime ?? Date.now(),
subscriptionOptions: {
auth: oldDetails.auth,
p256dh: oldDetails.p256dh,
endpoint: oldDetails.endpoint,
swScope: oldDetails.swScope,
vapidKey:
typeof oldDetails.vapidKey === 'string'
? oldDetails.vapidKey
: arrayToBase64(oldDetails.vapidKey)
}
};
} else if (db.oldVersion === 3) {
const oldDetails = value as V3TokenDetails;

tokenDetails = {
token: oldDetails.fcmToken,
createTime: oldDetails.createTime,
subscriptionOptions: {
auth: arrayToBase64(oldDetails.auth),
p256dh: arrayToBase64(oldDetails.p256dh),
endpoint: oldDetails.endpoint,
swScope: oldDetails.swScope,
vapidKey: arrayToBase64(oldDetails.vapidKey)
}
};
} else if (db.oldVersion === 4) {
const oldDetails = value as V4TokenDetails;

tokenDetails = {
token: oldDetails.fcmToken,
createTime: oldDetails.createTime,
subscriptionOptions: {
auth: arrayToBase64(oldDetails.auth),
p256dh: arrayToBase64(oldDetails.p256dh),
endpoint: oldDetails.endpoint,
swScope: oldDetails.swScope,
vapidKey: arrayToBase64(oldDetails.vapidKey)
if (oldVersion === 2) {
const oldDetails = value as V2TokenDetails;

if (!oldDetails.auth || !oldDetails.p256dh || !oldDetails.endpoint) {
return;
}
};

tokenDetails = {
token: oldDetails.fcmToken,
createTime: oldDetails.createTime ?? Date.now(),
subscriptionOptions: {
auth: oldDetails.auth,
p256dh: oldDetails.p256dh,
endpoint: oldDetails.endpoint,
swScope: oldDetails.swScope,
vapidKey:
typeof oldDetails.vapidKey === 'string'
? oldDetails.vapidKey
: arrayToBase64(oldDetails.vapidKey)
}
};
} else if (oldVersion === 3) {
const oldDetails = value as V3TokenDetails;

tokenDetails = {
token: oldDetails.fcmToken,
createTime: oldDetails.createTime,
subscriptionOptions: {
auth: arrayToBase64(oldDetails.auth),
p256dh: arrayToBase64(oldDetails.p256dh),
endpoint: oldDetails.endpoint,
swScope: oldDetails.swScope,
vapidKey: arrayToBase64(oldDetails.vapidKey)
}
};
} else if (oldVersion === 4) {
const oldDetails = value as V4TokenDetails;

tokenDetails = {
token: oldDetails.fcmToken,
createTime: oldDetails.createTime,
subscriptionOptions: {
auth: arrayToBase64(oldDetails.auth),
p256dh: arrayToBase64(oldDetails.p256dh),
endpoint: oldDetails.endpoint,
swScope: oldDetails.swScope,
vapidKey: arrayToBase64(oldDetails.vapidKey)
}
};
}
}
});
);
db.close();

// Delete all old databases.
await deleteDb(OLD_DB_NAME);
await deleteDb('fcm_vapid_details_db');
await deleteDb('undefined');
await deleteDB(OLD_DB_NAME);
await deleteDB('fcm_vapid_details_db');
await deleteDB('undefined');

return checkTokenDetails(tokenDetails) ? tokenDetails : null;
}
Expand Down
Loading

0 comments on commit 2d672ce

Please sign in to comment.