implement infrastructure for future DB migrations via backup
This commit is contained in:
parent
a5681579fb
commit
050999a910
@ -78,6 +78,7 @@ import {
|
|||||||
AcceptTipRequest,
|
AcceptTipRequest,
|
||||||
AbortPayWithRefundRequest,
|
AbortPayWithRefundRequest,
|
||||||
handleWorkerError,
|
handleWorkerError,
|
||||||
|
openPromise,
|
||||||
} from "taler-wallet-core";
|
} from "taler-wallet-core";
|
||||||
import { URL } from "url";
|
import { URL } from "url";
|
||||||
import axios, { AxiosError } from "axios";
|
import axios, { AxiosError } from "axios";
|
||||||
@ -94,7 +95,6 @@ import {
|
|||||||
import { ApplyRefundResponse } from "taler-wallet-core";
|
import { ApplyRefundResponse } from "taler-wallet-core";
|
||||||
import { PendingOperationsResponse } from "taler-wallet-core";
|
import { PendingOperationsResponse } from "taler-wallet-core";
|
||||||
import { CoinConfig } from "./denomStructures";
|
import { CoinConfig } from "./denomStructures";
|
||||||
import { after } from "taler-wallet-core/src/util/timer";
|
|
||||||
|
|
||||||
const exec = util.promisify(require("child_process").exec);
|
const exec = util.promisify(require("child_process").exec);
|
||||||
|
|
||||||
@ -1114,11 +1114,14 @@ export class ExchangeService implements ExchangeServiceInterface {
|
|||||||
`exchange-httpd-${this.name}`,
|
`exchange-httpd-${this.name}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await this.pingUntilAvailable();
|
||||||
await this.keyup();
|
await this.keyup();
|
||||||
}
|
}
|
||||||
|
|
||||||
async pingUntilAvailable(): Promise<void> {
|
async pingUntilAvailable(): Promise<void> {
|
||||||
const url = `http://localhost:${this.exchangeConfig.httpPort}/keys`;
|
// We request /management/keys, since /keys can block
|
||||||
|
// when we didn't do the key setup yet.
|
||||||
|
const url = `http://localhost:${this.exchangeConfig.httpPort}/management/keys`;
|
||||||
await pingProc(this.exchangeHttpProc, url, `exchange (${this.name})`);
|
await pingProc(this.exchangeHttpProc, url, `exchange (${this.name})`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1449,10 +1452,14 @@ export async function runTestWithState(
|
|||||||
): Promise<TestRunResult> {
|
): Promise<TestRunResult> {
|
||||||
const startMs = new Date().getTime();
|
const startMs = new Date().getTime();
|
||||||
|
|
||||||
const handleSignal = () => {
|
const p = openPromise();
|
||||||
|
let status: TestStatus;
|
||||||
|
|
||||||
|
const handleSignal = (s: string) => {
|
||||||
gc.shutdownSync();
|
gc.shutdownSync();
|
||||||
console.warn("**** received fatal signal, shutting down test harness");
|
console.warn("**** received fatal proces event, shutting down test harness");
|
||||||
process.exit(1);
|
status = "fail";
|
||||||
|
p.reject(Error("caught signal"));
|
||||||
};
|
};
|
||||||
|
|
||||||
process.on("SIGINT", handleSignal);
|
process.on("SIGINT", handleSignal);
|
||||||
@ -1460,10 +1467,9 @@ export async function runTestWithState(
|
|||||||
process.on("unhandledRejection", handleSignal);
|
process.on("unhandledRejection", handleSignal);
|
||||||
process.on("uncaughtException", handleSignal);
|
process.on("uncaughtException", handleSignal);
|
||||||
|
|
||||||
let status: TestStatus;
|
|
||||||
try {
|
try {
|
||||||
console.log("running test in directory", gc.testDir);
|
console.log("running test in directory", gc.testDir);
|
||||||
await testMain(gc);
|
await Promise.race([testMain(gc), p.promise]);
|
||||||
status = "pass";
|
status = "pass";
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("FATAL: test failed with exception", e);
|
console.error("FATAL: test failed with exception", e);
|
||||||
|
@ -66,12 +66,13 @@ const allTests: TestMainFunction[] = [
|
|||||||
runMerchantLongpollingTest,
|
runMerchantLongpollingTest,
|
||||||
runMerchantRefundApiTest,
|
runMerchantRefundApiTest,
|
||||||
runPayAbortTest,
|
runPayAbortTest,
|
||||||
runPayPaidTest,
|
|
||||||
runPaymentClaimTest,
|
runPaymentClaimTest,
|
||||||
runPaymentFaultTest,
|
runPaymentFaultTest,
|
||||||
runPaymentIdempotencyTest,
|
runPaymentIdempotencyTest,
|
||||||
runPaymentMultipleTest,
|
runPaymentMultipleTest,
|
||||||
|
runPaymentTest,
|
||||||
runPaymentTransientTest,
|
runPaymentTransientTest,
|
||||||
|
runPayPaidTest,
|
||||||
runPaywallFlowTest,
|
runPaywallFlowTest,
|
||||||
runRefundAutoTest,
|
runRefundAutoTest,
|
||||||
runRefundGoneTest,
|
runRefundGoneTest,
|
||||||
@ -82,10 +83,9 @@ const allTests: TestMainFunction[] = [
|
|||||||
runTimetravelWithdrawTest,
|
runTimetravelWithdrawTest,
|
||||||
runTippingTest,
|
runTippingTest,
|
||||||
runWallettestingTest,
|
runWallettestingTest,
|
||||||
|
runTestWithdrawalManualTest,
|
||||||
runWithdrawalAbortBankTest,
|
runWithdrawalAbortBankTest,
|
||||||
runWithdrawalBankIntegratedTest,
|
runWithdrawalBankIntegratedTest,
|
||||||
runWallettestingTest,
|
|
||||||
runPaymentTest,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export interface TestRunSpec {
|
export interface TestRunSpec {
|
||||||
@ -166,7 +166,12 @@ export async function runTests(spec: TestRunSpec) {
|
|||||||
JSON.stringify({ testResults }, undefined, 2),
|
JSON.stringify({ testResults }, undefined, 2),
|
||||||
);
|
);
|
||||||
console.log(`See ${resultsFile} for details`);
|
console.log(`See ${resultsFile} for details`);
|
||||||
|
console.log(`Skipped: ${numSkip}/${numTotal}`);
|
||||||
|
console.log(`Failed: ${numFail}/${numTotal}`);
|
||||||
console.log(`Passed: ${numPass}/${numTotal}`);
|
console.log(`Passed: ${numPass}/${numTotal}`);
|
||||||
|
if (numPass < numTotal - numSkip) {
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTestInfo(): TestInfo[] {
|
export function getTestInfo(): TestInfo[] {
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
import { Stores } from "./types/dbTypes";
|
import { MetaStores, Stores } from "./types/dbTypes";
|
||||||
import { openDatabase, Database, Store, Index } from "./util/query";
|
import {
|
||||||
|
openDatabase,
|
||||||
|
Database,
|
||||||
|
Store,
|
||||||
|
Index,
|
||||||
|
AnyStoreMap,
|
||||||
|
} from "./util/query";
|
||||||
import {
|
import {
|
||||||
IDBFactory,
|
IDBFactory,
|
||||||
IDBDatabase,
|
IDBDatabase,
|
||||||
@ -14,7 +20,11 @@ import { Logger } from "./util/logging";
|
|||||||
* for all previous versions must be written, which should be
|
* for all previous versions must be written, which should be
|
||||||
* avoided.
|
* avoided.
|
||||||
*/
|
*/
|
||||||
const TALER_DB_NAME = "taler-wallet-prod-v1";
|
const TALER_DB_NAME = "taler-wallet-main-v2";
|
||||||
|
|
||||||
|
const TALER_META_DB_NAME = "taler-wallet-meta";
|
||||||
|
|
||||||
|
const CURRENT_DB_CONFIG_KEY = "currentMainDbName";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current database minor version, should be incremented
|
* Current database minor version, should be incremented
|
||||||
@ -23,78 +33,134 @@ const TALER_DB_NAME = "taler-wallet-prod-v1";
|
|||||||
* backwards-compatible way or object stores and indices
|
* backwards-compatible way or object stores and indices
|
||||||
* are added.
|
* are added.
|
||||||
*/
|
*/
|
||||||
export const WALLET_DB_MINOR_VERSION = 3;
|
export const WALLET_DB_MINOR_VERSION = 1;
|
||||||
|
|
||||||
const logger = new Logger("db.ts");
|
const logger = new Logger("db.ts");
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
* Return a promise that resolves
|
||||||
* to the taler wallet db.
|
* to the taler wallet db.
|
||||||
*/
|
*/
|
||||||
export function openTalerDatabase(
|
export async function openTalerDatabase(
|
||||||
idbFactory: IDBFactory,
|
idbFactory: IDBFactory,
|
||||||
onVersionChange: () => void,
|
onVersionChange: () => void,
|
||||||
): Promise<IDBDatabase> {
|
): Promise<Database<typeof Stores>> {
|
||||||
const onUpgradeNeeded = (
|
const metaDbHandle = await openDatabase(
|
||||||
db: IDBDatabase,
|
idbFactory,
|
||||||
oldVersion: number,
|
TALER_META_DB_NAME,
|
||||||
newVersion: number,
|
1,
|
||||||
upgradeTransaction: IDBTransaction,
|
() => {},
|
||||||
): void => {
|
onMetaDbUpgradeNeeded,
|
||||||
if (oldVersion === 0) {
|
);
|
||||||
for (const n in Stores) {
|
|
||||||
if ((Stores as any)[n] instanceof Store) {
|
|
||||||
const si: Store<string, 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<string, string, any, any> = (si as any)[
|
|
||||||
indexName
|
|
||||||
];
|
|
||||||
s.createIndex(ii.indexName, ii.keyPath, ii.options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return openDatabase(
|
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(
|
||||||
idbFactory,
|
idbFactory,
|
||||||
TALER_DB_NAME,
|
TALER_DB_NAME,
|
||||||
WALLET_DB_MINOR_VERSION,
|
WALLET_DB_MINOR_VERSION,
|
||||||
onVersionChange,
|
onVersionChange,
|
||||||
onUpgradeNeeded,
|
onTalerDbUpgradeNeeded,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return new Database(mainDbHandle, Stores);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteTalerDatabase(idbFactory: IDBFactory): void {
|
export function deleteTalerDatabase(idbFactory: IDBFactory): void {
|
||||||
|
@ -34,6 +34,7 @@ import { NodeHttpLib } from "./NodeHttpLib";
|
|||||||
import { Logger } from "../util/logging";
|
import { Logger } from "../util/logging";
|
||||||
import { SynchronousCryptoWorkerFactory } from "../crypto/workers/synchronousWorker";
|
import { SynchronousCryptoWorkerFactory } from "../crypto/workers/synchronousWorker";
|
||||||
import type { IDBFactory } from "idb-bridge/lib/idbtypes";
|
import type { IDBFactory } from "idb-bridge/lib/idbtypes";
|
||||||
|
import { Stores } from "../types/dbTypes";
|
||||||
|
|
||||||
const logger = new Logger("headless/helpers.ts");
|
const logger = new Logger("headless/helpers.ts");
|
||||||
|
|
||||||
@ -149,9 +150,7 @@ export async function getDefaultNodeWallet(
|
|||||||
workerFactory = new SynchronousCryptoWorkerFactory();
|
workerFactory = new SynchronousCryptoWorkerFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
const dbWrap = new Database(myDb);
|
const w = new Wallet(myDb, myHttpLib, workerFactory);
|
||||||
|
|
||||||
const w = new Wallet(dbWrap, myHttpLib, workerFactory);
|
|
||||||
if (args.notifyHandler) {
|
if (args.notifyHandler) {
|
||||||
w.addNotificationListener(args.notifyHandler);
|
w.addNotificationListener(args.notifyHandler);
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ export {
|
|||||||
export * from "./operations/versions";
|
export * from "./operations/versions";
|
||||||
|
|
||||||
export * from "./db";
|
export * from "./db";
|
||||||
|
export * from "./types/dbTypes";
|
||||||
|
|
||||||
// Internationalization
|
// Internationalization
|
||||||
export * from "./i18n";
|
export * from "./i18n";
|
||||||
|
@ -29,6 +29,7 @@ import {
|
|||||||
DenominationStatus,
|
DenominationStatus,
|
||||||
WireFee,
|
WireFee,
|
||||||
ExchangeUpdateReason,
|
ExchangeUpdateReason,
|
||||||
|
MetaStores,
|
||||||
} from "../types/dbTypes";
|
} from "../types/dbTypes";
|
||||||
import { canonicalizeBaseUrl } from "../util/helpers";
|
import { canonicalizeBaseUrl } from "../util/helpers";
|
||||||
import * as Amounts from "../util/amounts";
|
import * as Amounts from "../util/amounts";
|
||||||
|
@ -23,6 +23,7 @@ import { PendingOperationsResponse } from "../types/pendingTypes";
|
|||||||
import { WalletNotification } from "../types/notifications";
|
import { WalletNotification } from "../types/notifications";
|
||||||
import { Database } from "../util/query";
|
import { Database } from "../util/query";
|
||||||
import { openPromise, OpenedPromise } from "../util/promiseUtils";
|
import { openPromise, OpenedPromise } from "../util/promiseUtils";
|
||||||
|
import { Stores } from "../types/dbTypes";
|
||||||
|
|
||||||
type NotificationListener = (n: WalletNotification) => void;
|
type NotificationListener = (n: WalletNotification) => void;
|
||||||
|
|
||||||
@ -59,7 +60,7 @@ export class InternalWalletState {
|
|||||||
// the actual value nullable.
|
// the actual value nullable.
|
||||||
// Check if we are in a DB migration / garbage collection
|
// Check if we are in a DB migration / garbage collection
|
||||||
// and throw an error in that case.
|
// and throw an error in that case.
|
||||||
public db: Database,
|
public db: Database<typeof Stores>,
|
||||||
public http: HttpRequestLibrary,
|
public http: HttpRequestLibrary,
|
||||||
cryptoWorkerFactory: CryptoWorkerFactory,
|
cryptoWorkerFactory: CryptoWorkerFactory,
|
||||||
) {
|
) {
|
||||||
|
@ -1591,9 +1591,6 @@ class TipsStore extends Store<"tips", TipRecord> {
|
|||||||
this,
|
this,
|
||||||
"tipsByMerchantTipIdAndOriginIndex",
|
"tipsByMerchantTipIdAndOriginIndex",
|
||||||
["merchantTipId", "merchantBaseUrl"],
|
["merchantTipId", "merchantBaseUrl"],
|
||||||
{
|
|
||||||
versionAdded: 2,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1657,7 +1654,7 @@ class BackupProvidersStore extends Store<
|
|||||||
BackupProviderRecord
|
BackupProviderRecord
|
||||||
> {
|
> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("backupProviders", { keyPath: "baseUrl", versionAdded: 3 });
|
super("backupProviders", { keyPath: "baseUrl" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1688,3 +1685,13 @@ export const Stores = {
|
|||||||
bankWithdrawUris: new BankWithdrawUrisStore(),
|
bankWithdrawUris: new BankWithdrawUrisStore(),
|
||||||
backupProviders: new BackupProvidersStore(),
|
backupProviders: new BackupProvidersStore(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export class MetaConfigStore extends Store<"metaConfig", ConfigRecord<any>> {
|
||||||
|
constructor() {
|
||||||
|
super("metaConfig", { keyPath: "key" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MetaStores = {
|
||||||
|
metaConfig: new MetaConfigStore(),
|
||||||
|
};
|
||||||
|
@ -269,6 +269,8 @@ class ResultStream<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type AnyStoreMap = { [s: string]: Store<any, any> };
|
||||||
|
|
||||||
type StoreName<S> = S extends Store<infer N, any> ? N : never;
|
type StoreName<S> = S extends Store<infer N, any> ? N : never;
|
||||||
type StoreContent<S> = S extends Store<any, infer R> ? R : never;
|
type StoreContent<S> = S extends Store<any, infer R> ? R : never;
|
||||||
type IndexRecord<Ind> = Ind extends Index<any, any, any, infer R> ? R : never;
|
type IndexRecord<Ind> = Ind extends Index<any, any, any, infer R> ? R : never;
|
||||||
@ -462,8 +464,7 @@ export class Index<
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a promise that resolves
|
* Return a promise that resolves to the opened IndexedDB database.
|
||||||
* to the taler wallet db.
|
|
||||||
*/
|
*/
|
||||||
export function openDatabase(
|
export function openDatabase(
|
||||||
idbFactory: IDBFactory,
|
idbFactory: IDBFactory,
|
||||||
@ -480,7 +481,7 @@ export function openDatabase(
|
|||||||
return new Promise<IDBDatabase>((resolve, reject) => {
|
return new Promise<IDBDatabase>((resolve, reject) => {
|
||||||
const req = idbFactory.open(databaseName, databaseVersion);
|
const req = idbFactory.open(databaseName, databaseVersion);
|
||||||
req.onerror = (e) => {
|
req.onerror = (e) => {
|
||||||
logger.error("taler database error", e);
|
logger.error("database error", e);
|
||||||
reject(new Error("database error"));
|
reject(new Error("database error"));
|
||||||
};
|
};
|
||||||
req.onsuccess = (e) => {
|
req.onsuccess = (e) => {
|
||||||
@ -508,8 +509,8 @@ export function openDatabase(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Database {
|
export class Database<StoreMap extends AnyStoreMap> {
|
||||||
constructor(private db: IDBDatabase) {}
|
constructor(private db: IDBDatabase, stores: StoreMap) {}
|
||||||
|
|
||||||
static deleteDatabase(idbFactory: IDBFactory, dbName: string): void {
|
static deleteDatabase(idbFactory: IDBFactory, dbName: string): void {
|
||||||
idbFactory.deleteDatabase(dbName);
|
idbFactory.deleteDatabase(dbName);
|
||||||
@ -571,10 +572,10 @@ export class Database {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async get<N extends string, T>(
|
async get<N extends keyof StoreMap, S extends StoreMap[N]>(
|
||||||
store: Store<N, T>,
|
store: S,
|
||||||
key: IDBValidKey,
|
key: IDBValidKey,
|
||||||
): Promise<T | undefined> {
|
): Promise<StoreContent<S> | undefined> {
|
||||||
const tx = this.db.transaction([store.name], "readonly");
|
const tx = this.db.transaction([store.name], "readonly");
|
||||||
const req = tx.objectStore(store.name).get(key);
|
const req = tx.objectStore(store.name).get(key);
|
||||||
const v = await requestToPromise(req);
|
const v = await requestToPromise(req);
|
||||||
@ -634,14 +635,22 @@ export class Database {
|
|||||||
return new ResultStream<IndexRecord<Ind>>(req);
|
return new ResultStream<IndexRecord<Ind>>(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
async runWithReadTransaction<T, StoreTypes extends Store<string, any>>(
|
async runWithReadTransaction<
|
||||||
|
T,
|
||||||
|
N extends keyof StoreMap,
|
||||||
|
StoreTypes extends StoreMap[N]
|
||||||
|
>(
|
||||||
stores: StoreTypes[],
|
stores: StoreTypes[],
|
||||||
f: (t: TransactionHandle<StoreTypes>) => Promise<T>,
|
f: (t: TransactionHandle<StoreTypes>) => Promise<T>,
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
return runWithTransaction<T, StoreTypes>(this.db, stores, f, "readonly");
|
return runWithTransaction<T, StoreTypes>(this.db, stores, f, "readonly");
|
||||||
}
|
}
|
||||||
|
|
||||||
async runWithWriteTransaction<T, StoreTypes extends Store<string, any>>(
|
async runWithWriteTransaction<
|
||||||
|
T,
|
||||||
|
N extends keyof StoreMap,
|
||||||
|
StoreTypes extends StoreMap[N]
|
||||||
|
>(
|
||||||
stores: StoreTypes[],
|
stores: StoreTypes[],
|
||||||
f: (t: TransactionHandle<StoreTypes>) => Promise<T>,
|
f: (t: TransactionHandle<StoreTypes>) => Promise<T>,
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
import { CryptoWorkerFactory } from "./crypto/workers/cryptoApi";
|
import { CryptoWorkerFactory } from "./crypto/workers/cryptoApi";
|
||||||
import { HttpRequestLibrary } from "./util/http";
|
import { HttpRequestLibrary } from "./util/http";
|
||||||
import { Database } from "./util/query";
|
import { Database, Store } from "./util/query";
|
||||||
|
|
||||||
import { Amounts, AmountJson } from "./util/amounts";
|
import { Amounts, AmountJson } from "./util/amounts";
|
||||||
|
|
||||||
@ -52,6 +52,7 @@ import {
|
|||||||
ReserveRecordStatus,
|
ReserveRecordStatus,
|
||||||
CoinSourceType,
|
CoinSourceType,
|
||||||
RefundState,
|
RefundState,
|
||||||
|
MetaStores,
|
||||||
} from "./types/dbTypes";
|
} from "./types/dbTypes";
|
||||||
import { CoinDumpJson, WithdrawUriInfoResponse } from "./types/talerTypes";
|
import { CoinDumpJson, WithdrawUriInfoResponse } from "./types/talerTypes";
|
||||||
import {
|
import {
|
||||||
@ -200,12 +201,12 @@ export class Wallet {
|
|||||||
private stopped = false;
|
private stopped = false;
|
||||||
private memoRunRetryLoop = new AsyncOpMemoSingle<void>();
|
private memoRunRetryLoop = new AsyncOpMemoSingle<void>();
|
||||||
|
|
||||||
get db(): Database {
|
get db(): Database<typeof Stores> {
|
||||||
return this.ws.db;
|
return this.ws.db;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
db: Database,
|
db: Database<typeof Stores>,
|
||||||
http: HttpRequestLibrary,
|
http: HttpRequestLibrary,
|
||||||
cryptoWorkerFactory: CryptoWorkerFactory,
|
cryptoWorkerFactory: CryptoWorkerFactory,
|
||||||
) {
|
) {
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import { isFirefox, getPermissionsApi } from "./compat";
|
import { isFirefox, getPermissionsApi } from "./compat";
|
||||||
import MessageSender = chrome.runtime.MessageSender;
|
|
||||||
import { extendedPermissions } from "./permissions";
|
import { extendedPermissions } from "./permissions";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -40,6 +39,7 @@ import {
|
|||||||
CoreApiResponse,
|
CoreApiResponse,
|
||||||
WalletDiagnostics,
|
WalletDiagnostics,
|
||||||
CoreApiResponseSuccess,
|
CoreApiResponseSuccess,
|
||||||
|
Stores,
|
||||||
} from "taler-wallet-core";
|
} from "taler-wallet-core";
|
||||||
import { BrowserHttpLib } from "./browserHttpLib";
|
import { BrowserHttpLib } from "./browserHttpLib";
|
||||||
import { BrowserCryptoWorkerFactory } from "./browserCryptoWorkerFactory";
|
import { BrowserCryptoWorkerFactory } from "./browserCryptoWorkerFactory";
|
||||||
@ -50,7 +50,7 @@ import { BrowserCryptoWorkerFactory } from "./browserCryptoWorkerFactory";
|
|||||||
*/
|
*/
|
||||||
let currentWallet: Wallet | undefined;
|
let currentWallet: Wallet | undefined;
|
||||||
|
|
||||||
let currentDatabase: IDBDatabase | undefined;
|
let currentDatabase: Database<typeof Stores> | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Last version if an outdated DB, if applicable.
|
* Last version if an outdated DB, if applicable.
|
||||||
@ -135,7 +135,7 @@ async function dispatch(
|
|||||||
setupHeaderListener();
|
setupHeaderListener();
|
||||||
r = wrapResponse({ newValue: true });
|
r = wrapResponse({ newValue: true });
|
||||||
} else {
|
} else {
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
getPermissionsApi().remove(extendedPermissions, (rem) => {
|
getPermissionsApi().remove(extendedPermissions, (rem) => {
|
||||||
console.log("permissions removed:", rem);
|
console.log("permissions removed:", rem);
|
||||||
resolve();
|
resolve();
|
||||||
@ -246,7 +246,7 @@ async function reinitWallet(): Promise<void> {
|
|||||||
const http = new BrowserHttpLib();
|
const http = new BrowserHttpLib();
|
||||||
console.log("setting wallet");
|
console.log("setting wallet");
|
||||||
const wallet = new Wallet(
|
const wallet = new Wallet(
|
||||||
new Database(currentDatabase),
|
currentDatabase,
|
||||||
http,
|
http,
|
||||||
new BrowserCryptoWorkerFactory(),
|
new BrowserCryptoWorkerFactory(),
|
||||||
);
|
);
|
||||||
|
@ -31,28 +31,6 @@ importers:
|
|||||||
specifiers:
|
specifiers:
|
||||||
'@types/node': ^14.14.7
|
'@types/node': ^14.14.7
|
||||||
typescript: ^4.0.5
|
typescript: ^4.0.5
|
||||||
packages/taler-integrationtests:
|
|
||||||
dependencies:
|
|
||||||
axios: 0.21.0
|
|
||||||
taler-wallet-core: link:../taler-wallet-core
|
|
||||||
tslib: 2.0.3
|
|
||||||
devDependencies:
|
|
||||||
esm: 3.2.25
|
|
||||||
nyc: 15.1.0
|
|
||||||
prettier: 2.1.2
|
|
||||||
source-map-support: 0.5.19
|
|
||||||
ts-node: 9.0.0_typescript@4.0.5
|
|
||||||
typescript: 4.0.5
|
|
||||||
specifiers:
|
|
||||||
axios: ^0.21.0
|
|
||||||
esm: ^3.2.25
|
|
||||||
nyc: ^15.1.0
|
|
||||||
prettier: ^2.1.2
|
|
||||||
source-map-support: ^0.5.19
|
|
||||||
taler-wallet-core: workspace:*
|
|
||||||
ts-node: ^9.0.0
|
|
||||||
tslib: ^2.0.3
|
|
||||||
typescript: ^4.0.5
|
|
||||||
packages/taler-wallet-android:
|
packages/taler-wallet-android:
|
||||||
dependencies:
|
dependencies:
|
||||||
taler-wallet-core: link:../taler-wallet-core
|
taler-wallet-core: link:../taler-wallet-core
|
||||||
@ -1031,10 +1009,6 @@ packages:
|
|||||||
dev: true
|
dev: true
|
||||||
resolution:
|
resolution:
|
||||||
integrity: sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=
|
integrity: sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=
|
||||||
/arg/4.1.3:
|
|
||||||
dev: true
|
|
||||||
resolution:
|
|
||||||
integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
|
|
||||||
/argparse/1.0.10:
|
/argparse/1.0.10:
|
||||||
dependencies:
|
dependencies:
|
||||||
sprintf-js: 1.0.3
|
sprintf-js: 1.0.3
|
||||||
@ -1737,12 +1711,6 @@ packages:
|
|||||||
node: '>=10'
|
node: '>=10'
|
||||||
resolution:
|
resolution:
|
||||||
integrity: sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==
|
integrity: sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==
|
||||||
/diff/4.0.2:
|
|
||||||
dev: true
|
|
||||||
engines:
|
|
||||||
node: '>=0.3.1'
|
|
||||||
resolution:
|
|
||||||
integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
|
|
||||||
/dir-glob/3.0.1:
|
/dir-glob/3.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
path-type: 4.0.0
|
path-type: 4.0.0
|
||||||
@ -3352,10 +3320,6 @@ packages:
|
|||||||
node: '>=8'
|
node: '>=8'
|
||||||
resolution:
|
resolution:
|
||||||
integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
|
integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
|
||||||
/make-error/1.3.6:
|
|
||||||
dev: true
|
|
||||||
resolution:
|
|
||||||
integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
|
|
||||||
/map-age-cleaner/0.1.3:
|
/map-age-cleaner/0.1.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
p-defer: 1.0.0
|
p-defer: 1.0.0
|
||||||
@ -4788,22 +4752,6 @@ packages:
|
|||||||
node: '>=0.10.0'
|
node: '>=0.10.0'
|
||||||
resolution:
|
resolution:
|
||||||
integrity: sha1-n5up2e+odkw4dpi8v+sshI8RrbM=
|
integrity: sha1-n5up2e+odkw4dpi8v+sshI8RrbM=
|
||||||
/ts-node/9.0.0_typescript@4.0.5:
|
|
||||||
dependencies:
|
|
||||||
arg: 4.1.3
|
|
||||||
diff: 4.0.2
|
|
||||||
make-error: 1.3.6
|
|
||||||
source-map-support: 0.5.19
|
|
||||||
typescript: 4.0.5
|
|
||||||
yn: 3.1.1
|
|
||||||
dev: true
|
|
||||||
engines:
|
|
||||||
node: '>=10.0.0'
|
|
||||||
hasBin: true
|
|
||||||
peerDependencies:
|
|
||||||
typescript: '>=2.7'
|
|
||||||
resolution:
|
|
||||||
integrity: sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==
|
|
||||||
/tsconfig-paths/3.9.0:
|
/tsconfig-paths/3.9.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/json5': 0.0.29
|
'@types/json5': 0.0.29
|
||||||
@ -5144,12 +5092,6 @@ packages:
|
|||||||
node: '>=10'
|
node: '>=10'
|
||||||
resolution:
|
resolution:
|
||||||
integrity: sha512-hAD1RcFP/wfgfxgMVswPE+z3tlPFtxG8/yWUrG2i17sTWGCGqWnxKcLTF4cUKDUK8fzokwsmO9H0TDkRbMHy8w==
|
integrity: sha512-hAD1RcFP/wfgfxgMVswPE+z3tlPFtxG8/yWUrG2i17sTWGCGqWnxKcLTF4cUKDUK8fzokwsmO9H0TDkRbMHy8w==
|
||||||
/yn/3.1.1:
|
|
||||||
dev: true
|
|
||||||
engines:
|
|
||||||
node: '>=6'
|
|
||||||
resolution:
|
|
||||||
integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
|
|
||||||
/z-schema/3.18.4:
|
/z-schema/3.18.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
lodash.get: 4.4.2
|
lodash.get: 4.4.2
|
||||||
|
Loading…
Reference in New Issue
Block a user