prepare for schema migrations
This commit is contained in:
parent
20ebc44420
commit
49e3b3e5b9
35
src/db.ts
35
src/db.ts
@ -1,7 +1,7 @@
|
|||||||
import { Stores } from "./types/dbTypes";
|
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
|
* 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
|
* In the future we might consider adding migration functions for
|
||||||
* each version increment.
|
* each version increment.
|
||||||
*/
|
*/
|
||||||
export const WALLET_DB_VERSION = 28;
|
export const WALLET_DB_VERSION = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a promise that resolves
|
* Return a promise that resolves
|
||||||
@ -18,15 +18,38 @@ export const WALLET_DB_VERSION = 28;
|
|||||||
export function openTalerDatabase(
|
export function openTalerDatabase(
|
||||||
idbFactory: IDBFactory,
|
idbFactory: IDBFactory,
|
||||||
onVersionChange: () => void,
|
onVersionChange: () => void,
|
||||||
onUpgradeUnsupported: (oldVersion: number, newVersion: number) => void,
|
|
||||||
): Promise<IDBDatabase> {
|
): 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(
|
return openDatabase(
|
||||||
idbFactory,
|
idbFactory,
|
||||||
TALER_DB_NAME,
|
TALER_DB_NAME,
|
||||||
WALLET_DB_VERSION,
|
WALLET_DB_VERSION,
|
||||||
Stores,
|
|
||||||
onVersionChange,
|
onVersionChange,
|
||||||
onUpgradeUnsupported,
|
onUpgradeNeeded,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,9 +25,7 @@
|
|||||||
import { Wallet } from "../wallet";
|
import { Wallet } from "../wallet";
|
||||||
import { MemoryBackend, BridgeIDBFactory, shimIndexedDB } from "idb-bridge";
|
import { MemoryBackend, BridgeIDBFactory, shimIndexedDB } from "idb-bridge";
|
||||||
import { openTalerDatabase } from "../db";
|
import { openTalerDatabase } from "../db";
|
||||||
import {
|
import { HttpRequestLibrary } from "../util/http";
|
||||||
HttpRequestLibrary,
|
|
||||||
} from "../util/http";
|
|
||||||
import * as amounts from "../util/amounts";
|
import * as amounts from "../util/amounts";
|
||||||
import { Bank } from "./bank";
|
import { Bank } from "./bank";
|
||||||
import fs = require("fs");
|
import fs = require("fs");
|
||||||
@ -39,7 +37,6 @@ import { Logger } from "../util/logging";
|
|||||||
|
|
||||||
const logger = new Logger("helpers.ts");
|
const logger = new Logger("helpers.ts");
|
||||||
|
|
||||||
|
|
||||||
export interface DefaultNodeWalletArgs {
|
export interface DefaultNodeWalletArgs {
|
||||||
/**
|
/**
|
||||||
* Location of the wallet database.
|
* Location of the wallet database.
|
||||||
@ -112,18 +109,9 @@ export async function getDefaultNodeWallet(
|
|||||||
throw Error();
|
throw Error();
|
||||||
};
|
};
|
||||||
|
|
||||||
const myUnsupportedUpgrade = () => {
|
|
||||||
console.error("unsupported database migration");
|
|
||||||
throw Error();
|
|
||||||
};
|
|
||||||
|
|
||||||
shimIndexedDB(myBridgeIdbFactory);
|
shimIndexedDB(myBridgeIdbFactory);
|
||||||
|
|
||||||
const myDb = await openTalerDatabase(
|
const myDb = await openTalerDatabase(myIdbFactory, myVersionChange);
|
||||||
myIdbFactory,
|
|
||||||
myVersionChange,
|
|
||||||
myUnsupportedUpgrade,
|
|
||||||
);
|
|
||||||
|
|
||||||
//const worker = new SynchronousCryptoWorkerFactory();
|
//const worker = new SynchronousCryptoWorkerFactory();
|
||||||
//const worker = new NodeCryptoWorkerFactory();
|
//const worker = new NodeCryptoWorkerFactory();
|
||||||
|
@ -1366,6 +1366,34 @@ export interface BankWithdrawUriRecord {
|
|||||||
reservePub: string;
|
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 */
|
/* 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 coins = new CoinsStore();
|
||||||
export const coinsReturns = new Store<CoinsReturnRecord>("coinsReturns", {
|
export const coinsReturns = new Store<CoinsReturnRecord>("coinsReturns", {
|
||||||
keyPath: "contractTermsHash",
|
keyPath: "contractTermsHash",
|
||||||
@ -1539,6 +1573,7 @@ export namespace Stores {
|
|||||||
export const payEvents = new PayEventsStore();
|
export const payEvents = new PayEventsStore();
|
||||||
export const reserveUpdatedEvents = new ReserveUpdatedEventsStore();
|
export const reserveUpdatedEvents = new ReserveUpdatedEventsStore();
|
||||||
export const exchangeUpdatedEvents = new ExchangeUpdatedEventsStore();
|
export const exchangeUpdatedEvents = new ExchangeUpdatedEventsStore();
|
||||||
|
export const walletImports = new WalletImportsStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* tslint:enable:completed-docs */
|
/* tslint:enable:completed-docs */
|
||||||
|
58
src/types/schemacore.ts
Normal file
58
src/types/schemacore.ts
Normal 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;
|
||||||
|
}
|
@ -383,9 +383,8 @@ export function openDatabase(
|
|||||||
idbFactory: IDBFactory,
|
idbFactory: IDBFactory,
|
||||||
databaseName: string,
|
databaseName: string,
|
||||||
databaseVersion: number,
|
databaseVersion: number,
|
||||||
schema: any,
|
|
||||||
onVersionChange: () => void,
|
onVersionChange: () => void,
|
||||||
onUpgradeUnsupported: (oldVersion: number, newVersion: number) => void,
|
onUpgradeNeeded: (db: IDBDatabase, oldVersion: number, newVersion: number) => void,
|
||||||
): Promise<IDBDatabase> {
|
): Promise<IDBDatabase> {
|
||||||
return new Promise<IDBDatabase>((resolve, reject) => {
|
return new Promise<IDBDatabase>((resolve, reject) => {
|
||||||
const req = idbFactory.open(databaseName, databaseVersion);
|
const req = idbFactory.open(databaseName, databaseVersion);
|
||||||
@ -405,31 +404,10 @@ export function openDatabase(
|
|||||||
};
|
};
|
||||||
req.onupgradeneeded = e => {
|
req.onupgradeneeded = e => {
|
||||||
const db = req.result;
|
const db = req.result;
|
||||||
|
onUpgradeNeeded(db, e.oldVersion, e.newVersion!);
|
||||||
console.log(
|
console.log(
|
||||||
`DB: upgrade needed: oldVersion=${e.oldVersion}, newVersion=${e.newVersion}`,
|
`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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -390,16 +390,6 @@ let outdatedDbVersion: number | undefined;
|
|||||||
|
|
||||||
let walletInit: OpenedPromise<void> = openPromise<void>();
|
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() {
|
async function reinitWallet() {
|
||||||
if (currentWallet) {
|
if (currentWallet) {
|
||||||
currentWallet.stop();
|
currentWallet.stop();
|
||||||
@ -412,7 +402,6 @@ async function reinitWallet() {
|
|||||||
currentDatabase = await openTalerDatabase(
|
currentDatabase = await openTalerDatabase(
|
||||||
indexedDB,
|
indexedDB,
|
||||||
reinitWallet,
|
reinitWallet,
|
||||||
handleUpgradeUnsupported,
|
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("could not open database", e);
|
console.error("could not open database", e);
|
||||||
|
@ -67,6 +67,7 @@
|
|||||||
"src/types/history.ts",
|
"src/types/history.ts",
|
||||||
"src/types/notifications.ts",
|
"src/types/notifications.ts",
|
||||||
"src/types/pending.ts",
|
"src/types/pending.ts",
|
||||||
|
"src/types/schemacore.ts",
|
||||||
"src/types/talerTypes.ts",
|
"src/types/talerTypes.ts",
|
||||||
"src/types/types-test.ts",
|
"src/types/types-test.ts",
|
||||||
"src/types/walletTypes.ts",
|
"src/types/walletTypes.ts",
|
||||||
|
Loading…
Reference in New Issue
Block a user