wallet-core/packages/taler-wallet-core/src/db.ts

169 lines
4.5 KiB
TypeScript
Raw Normal View History

import { MetaStores, Stores } from "./types/dbTypes";
import {
openDatabase,
Database,
Store,
Index,
AnyStoreMap,
} from "./util/query";
2020-12-02 14:55:04 +01:00
import {
IDBFactory,
IDBDatabase,
IDBObjectStore,
IDBTransaction,
} from "idb-bridge";
import { Logger } from "./util/logging";
2019-07-31 01:33:56 +02:00
2020-05-08 14:15:23 +02:00
/**
2020-09-08 17:46:11 +02:00
* Name of the Taler database. This is effectively the major
* version of the DB schema. Whenever it changes, custom import logic
* for all previous versions must be written, which should be
* avoided.
2020-05-08 14:15:23 +02:00
*/
const TALER_DB_NAME = "taler-wallet-main-v2";
const TALER_META_DB_NAME = "taler-wallet-meta";
const CURRENT_DB_CONFIG_KEY = "currentMainDbName";
2019-07-31 01:33:56 +02:00
2019-12-13 13:10:20 +01:00
/**
2020-05-08 14:15:23 +02:00
* Current database minor version, should be incremented
* each time we do minor schema changes on the database.
* A change is considered minor when fields are added in a
* backwards-compatible way or object stores and indices
* are added.
2019-12-13 13:10:20 +01:00
*/
export const WALLET_DB_MINOR_VERSION = 1;
2020-11-16 14:12:37 +01:00
const logger = new Logger("db.ts");
2019-12-13 13:10:20 +01:00
function upgradeFromStoreMap(
storeMap: AnyStoreMap,
db: IDBDatabase,
oldVersion: number,
newVersion: number,
upgradeTransaction: IDBTransaction,
): void {
if (oldVersion === 0) {
for (const n in storeMap) {
if ((storeMap as any)[n] instanceof Store) {
const si: Store<string, any> = (storeMap as any)[n];
const s = db.createObjectStore(si.name, si.storeParams);
for (const indexName in si as any) {
if ((si as any)[indexName] instanceof Index) {
const ii: Index<string, string, any, any> = (si as any)[indexName];
s.createIndex(ii.indexName, ii.keyPath, ii.options);
2020-11-16 14:12:37 +01:00
}
}
}
}
return;
}
if (oldVersion === newVersion) {
return;
}
logger.info(`upgrading database from ${oldVersion} to ${newVersion}`);
for (const n in Stores) {
if ((Stores as any)[n] instanceof Store) {
const si: Store<string, any> = (Stores as any)[n];
let s: IDBObjectStore;
const storeVersionAdded = si.storeParams?.versionAdded ?? 1;
if (storeVersionAdded > oldVersion) {
s = db.createObjectStore(si.name, si.storeParams);
} else {
s = upgradeTransaction.objectStore(si.name);
}
for (const indexName in si as any) {
if ((si as any)[indexName] instanceof Index) {
const ii: Index<string, string, any, any> = (si as any)[indexName];
const indexVersionAdded = ii.options?.versionAdded ?? 0;
if (
indexVersionAdded > oldVersion ||
storeVersionAdded > oldVersion
) {
s.createIndex(ii.indexName, ii.keyPath, ii.options);
2019-12-19 13:48:37 +01:00
}
}
2020-11-16 14:12:37 +01:00
}
2019-12-19 13:48:37 +01:00
}
}
}
2019-12-19 13:48:37 +01:00
function onTalerDbUpgradeNeeded(
db: IDBDatabase,
oldVersion: number,
newVersion: number,
upgradeTransaction: IDBTransaction,
) {
upgradeFromStoreMap(Stores, db, oldVersion, newVersion, upgradeTransaction);
}
function onMetaDbUpgradeNeeded(
db: IDBDatabase,
oldVersion: number,
newVersion: number,
upgradeTransaction: IDBTransaction,
) {
upgradeFromStoreMap(
MetaStores,
db,
oldVersion,
newVersion,
upgradeTransaction,
);
}
/**
* Return a promise that resolves
* to the taler wallet db.
*/
export async function openTalerDatabase(
idbFactory: IDBFactory,
onVersionChange: () => void,
): Promise<Database<typeof Stores>> {
const metaDbHandle = await openDatabase(
idbFactory,
TALER_META_DB_NAME,
1,
() => {},
onMetaDbUpgradeNeeded,
);
const metaDb = new Database(metaDbHandle, MetaStores);
let currentMainVersion: string | undefined;
await metaDb.runWithWriteTransaction([MetaStores.metaConfig], async (tx) => {
const dbVersionRecord = await tx.get(
MetaStores.metaConfig,
CURRENT_DB_CONFIG_KEY,
);
if (!dbVersionRecord) {
currentMainVersion = TALER_DB_NAME;
await tx.put(MetaStores.metaConfig, {
key: CURRENT_DB_CONFIG_KEY,
value: TALER_DB_NAME,
});
} else {
currentMainVersion = dbVersionRecord.key;
}
});
if (currentMainVersion !== TALER_DB_NAME) {
// In the future, the migration logic will be implemented here.
throw Error(`migration from database ${currentMainVersion} not supported`);
}
const mainDbHandle = await openDatabase(
2019-12-12 22:39:45 +01:00
idbFactory,
TALER_DB_NAME,
WALLET_DB_MINOR_VERSION,
2019-12-12 22:39:45 +01:00
onVersionChange,
onTalerDbUpgradeNeeded,
2019-12-12 22:39:45 +01:00
);
return new Database(mainDbHandle, Stores);
2019-07-31 01:33:56 +02:00
}
export function deleteTalerDatabase(idbFactory: IDBFactory): void {
2019-12-12 22:39:45 +01:00
Database.deleteDatabase(idbFactory, TALER_DB_NAME);
2019-12-19 13:48:37 +01:00
}