prepare for schema migrations

This commit is contained in:
Florian Dold 2019-12-19 13:48:37 +01:00
parent 20ebc44420
commit 49e3b3e5b9
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
7 changed files with 128 additions and 56 deletions

View File

@ -1,7 +1,7 @@
import { Stores } from "./types/dbTypes";
import { openDatabase, Database } from "./util/query";
import { openDatabase, Database, Store, Index } from "./util/query";
const TALER_DB_NAME = "taler";
const TALER_DB_NAME = "taler-wallet";
/**
* Current database version, should be incremented
@ -9,7 +9,7 @@ const TALER_DB_NAME = "taler";
* In the future we might consider adding migration functions for
* each version increment.
*/
export const WALLET_DB_VERSION = 28;
export const WALLET_DB_VERSION = 1;
/**
* Return a promise that resolves
@ -18,18 +18,41 @@ export const WALLET_DB_VERSION = 28;
export function openTalerDatabase(
idbFactory: IDBFactory,
onVersionChange: () => void,
onUpgradeUnsupported: (oldVersion: number, newVersion: number) => void,
): Promise<IDBDatabase> {
const onUpgradeNeeded = (
db: IDBDatabase,
oldVersion: number,
newVersion: number,
) => {
switch (oldVersion) {
case 0: // DB does not exist yet
for (const n in Stores) {
if ((Stores as any)[n] instanceof Store) {
const si: Store<any> = (Stores 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<any, any> = (si as any)[indexName];
s.createIndex(ii.indexName, ii.keyPath, ii.options);
}
}
}
}
break;
default:
throw Error("unsupported existig DB version");
}
};
return openDatabase(
idbFactory,
TALER_DB_NAME,
WALLET_DB_VERSION,
Stores,
onVersionChange,
onUpgradeUnsupported,
onUpgradeNeeded,
);
}
export function deleteTalerDatabase(idbFactory: IDBFactory) {
Database.deleteDatabase(idbFactory, TALER_DB_NAME);
}
}

View File

@ -25,9 +25,7 @@
import { Wallet } from "../wallet";
import { MemoryBackend, BridgeIDBFactory, shimIndexedDB } from "idb-bridge";
import { openTalerDatabase } from "../db";
import {
HttpRequestLibrary,
} from "../util/http";
import { HttpRequestLibrary } from "../util/http";
import * as amounts from "../util/amounts";
import { Bank } from "./bank";
import fs = require("fs");
@ -39,7 +37,6 @@ import { Logger } from "../util/logging";
const logger = new Logger("helpers.ts");
export interface DefaultNodeWalletArgs {
/**
* Location of the wallet database.
@ -112,18 +109,9 @@ export async function getDefaultNodeWallet(
throw Error();
};
const myUnsupportedUpgrade = () => {
console.error("unsupported database migration");
throw Error();
};
shimIndexedDB(myBridgeIdbFactory);
const myDb = await openTalerDatabase(
myIdbFactory,
myVersionChange,
myUnsupportedUpgrade,
);
const myDb = await openTalerDatabase(myIdbFactory, myVersionChange);
//const worker = new SynchronousCryptoWorkerFactory();
//const worker = new NodeCryptoWorkerFactory();

View File

@ -1366,6 +1366,34 @@ export interface BankWithdrawUriRecord {
reservePub: string;
}
export const enum ImportPayloadType {
CoreSchema = "core-schema",
}
/**
* Record to keep track of data imported into the wallet.
*/
export class WalletImportRecord {
/**
* Unique ID to reference this import record.
*/
walletImportId: string;
/**
* When was the data imported?
*/
timestampImportStarted: Timestamp;
timestampImportFinished: Timestamp | undefined;
payloadType: ImportPayloadType;
/**
* The actual data to import.
*/
payload: any;
}
/* tslint:disable:completed-docs */
/**
@ -1517,6 +1545,12 @@ export namespace Stores {
}
}
class WalletImportsStore extends Store<WalletImportRecord> {
constructor() {
super("walletImports", { keyPath: "walletImportId" });
}
}
export const coins = new CoinsStore();
export const coinsReturns = new Store<CoinsReturnRecord>("coinsReturns", {
keyPath: "contractTermsHash",
@ -1539,6 +1573,7 @@ export namespace Stores {
export const payEvents = new PayEventsStore();
export const reserveUpdatedEvents = new ReserveUpdatedEventsStore();
export const exchangeUpdatedEvents = new ExchangeUpdatedEventsStore();
export const walletImports = new WalletImportsStore();
}
/* tslint:enable:completed-docs */

58
src/types/schemacore.ts Normal file
View File

@ -0,0 +1,58 @@
/*
This file is part of GNU Taler
(C) 2019 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* Core of the wallet's schema, used for painless export, import
* and schema migration.
*
* If this schema is extended, it must be extended in a completely
* backwards-compatible way.
*/
interface CoreCoin {
exchangeBaseUrl: string;
coinPub: string;
coinPriv: string;
amountRemaining: string;
}
interface CorePurchase {
noncePub: string;
noncePriv: string;
paySig: string;
contractTerms: any;
}
interface CoreReserve {
reservePub: string;
reservePriv: string;
exchangeBaseUrl: string;
}
interface SchemaCore {
coins: CoreCoin[];
purchases: CorePurchase[];
/**
* Schema version (of full schema) of wallet that exported the core schema.
*/
versionExporter: number;
/**
* Schema version of the database that has been exported to the core schema
*/
versionSourceDatabase: number;
}

View File

@ -383,9 +383,8 @@ export function openDatabase(
idbFactory: IDBFactory,
databaseName: string,
databaseVersion: number,
schema: any,
onVersionChange: () => void,
onUpgradeUnsupported: (oldVersion: number, newVersion: number) => void,
onUpgradeNeeded: (db: IDBDatabase, oldVersion: number, newVersion: number) => void,
): Promise<IDBDatabase> {
return new Promise<IDBDatabase>((resolve, reject) => {
const req = idbFactory.open(databaseName, databaseVersion);
@ -405,31 +404,10 @@ export function openDatabase(
};
req.onupgradeneeded = e => {
const db = req.result;
onUpgradeNeeded(db, e.oldVersion, e.newVersion!);
console.log(
`DB: upgrade needed: oldVersion=${e.oldVersion}, newVersion=${e.newVersion}`,
);
switch (e.oldVersion) {
case 0: // DB does not exist yet
for (const n in schema) {
if (schema[n] instanceof Store) {
const si: Store<any> = schema[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<any, any> = (si as any)[indexName];
s.createIndex(ii.indexName, ii.keyPath, ii.options);
}
}
}
}
break;
default:
if (e.oldVersion !== databaseVersion) {
onUpgradeUnsupported(e.oldVersion, databaseVersion);
throw Error("incompatible DB");
}
break;
}
};
});
}

View File

@ -390,16 +390,6 @@ let outdatedDbVersion: number | undefined;
let walletInit: OpenedPromise<void> = openPromise<void>();
function handleUpgradeUnsupported(oldDbVersion: number, newDbVersion: number) {
console.log("DB migration not supported");
outdatedDbVersion = oldDbVersion;
chrome.tabs.create({
url: chrome.extension.getURL("/src/webex/pages/reset-required.html"),
});
setBadgeText({ text: "err" });
chrome.browserAction.setBadgeBackgroundColor({ color: "#F00" });
}
async function reinitWallet() {
if (currentWallet) {
currentWallet.stop();
@ -412,7 +402,6 @@ async function reinitWallet() {
currentDatabase = await openTalerDatabase(
indexedDB,
reinitWallet,
handleUpgradeUnsupported,
);
} catch (e) {
console.error("could not open database", e);

View File

@ -67,6 +67,7 @@
"src/types/history.ts",
"src/types/notifications.ts",
"src/types/pending.ts",
"src/types/schemacore.ts",
"src/types/talerTypes.ts",
"src/types/types-test.ts",
"src/types/walletTypes.ts",