summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/taler-wallet-core/src/db.ts56
-rw-r--r--packages/taler-wallet-core/src/operations/tip.ts6
-rw-r--r--packages/taler-wallet-core/src/types/dbTypes.ts13
-rw-r--r--packages/taler-wallet-core/src/util/helpers.ts4
-rw-r--r--packages/taler-wallet-core/src/util/query.ts28
-rw-r--r--packages/taler-wallet-core/src/util/taleruri.ts9
-rw-r--r--pnpm-lock.yaml2
7 files changed, 89 insertions, 29 deletions
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index b94fd1547..ac3c79e20 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -1,6 +1,7 @@
import { Stores } from "./types/dbTypes";
import { openDatabase, Database, Store, Index } from "./util/query";
-import { IDBFactory, IDBDatabase } from "idb-bridge";
+import { IDBFactory, IDBDatabase, IDBObjectStore, IDBTransaction } from "idb-bridge";
+import { Logger } from './util/logging';
/**
* Name of the Taler database. This is effectively the major
@@ -17,7 +18,9 @@ const TALER_DB_NAME = "taler-wallet-prod-v1";
* backwards-compatible way or object stores and indices
* are added.
*/
-export const WALLET_DB_MINOR_VERSION = 1;
+export const WALLET_DB_MINOR_VERSION = 2;
+
+const logger = new Logger("db.ts");
/**
* Return a promise that resolves
@@ -31,24 +34,45 @@ export function openTalerDatabase(
db: IDBDatabase,
oldVersion: number,
newVersion: number,
+ upgradeTransaction: IDBTransaction,
): void => {
- 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);
- }
+ if (oldVersion === 0) {
+ 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);
+ }
+ }
+ }
+ }
+ 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<any> = (Stores as any)[n];
+ let s: IDBObjectStore;
+ if ((si.storeParams?.versionAdded ?? 1) > 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<any, any> = (si as any)[indexName];
+ if ((ii.options?.versionAdded ?? 0) > oldVersion) {
+ s.createIndex(ii.indexName, ii.keyPath, ii.options);
}
}
}
- break;
- default:
- throw Error("unsupported existig DB version");
+ }
}
};
diff --git a/packages/taler-wallet-core/src/operations/tip.ts b/packages/taler-wallet-core/src/operations/tip.ts
index 08f45eb89..3b8ac0269 100644
--- a/packages/taler-wallet-core/src/operations/tip.ts
+++ b/packages/taler-wallet-core/src/operations/tip.ts
@@ -46,6 +46,7 @@ import { Logger } from "../util/logging";
import { checkDbInvariant } from "../util/invariants";
import { TalerErrorCode } from "../TalerErrorCode";
import { initRetryInfo, updateRetryInfoTimeout } from "../util/retries";
+import { j2s } from '../util/helpers';
const logger = new Logger("operations/tip.ts");
@@ -68,7 +69,7 @@ export async function prepareTip(
merchantResp,
codecForTipPickupGetResponse(),
);
- logger.trace(`status ${tipPickupStatus}`);
+ logger.trace(`status ${j2s(tipPickupStatus)}`);
const amount = Amounts.parseOrThrow(tipPickupStatus.tip_amount);
@@ -80,6 +81,7 @@ export async function prepareTip(
]);
if (!tipRecord) {
+ logger.trace("new tip, creating tip record");
await updateExchangeFromUrl(ws, tipPickupStatus.exchange_url);
const withdrawDetails = await getExchangeWithdrawalInfo(
ws,
@@ -232,7 +234,7 @@ async function processTipImpl(
}));
const tipStatusUrl = new URL(
- `/tips/${tipRecord.merchantTipId}/pickup`,
+ `tips/${tipRecord.merchantTipId}/pickup`,
tipRecord.merchantBaseUrl,
);
diff --git a/packages/taler-wallet-core/src/types/dbTypes.ts b/packages/taler-wallet-core/src/types/dbTypes.ts
index 8f6c22f22..ed3a18ae1 100644
--- a/packages/taler-wallet-core/src/types/dbTypes.ts
+++ b/packages/taler-wallet-core/src/types/dbTypes.ts
@@ -361,7 +361,6 @@ export enum DenominationStatus {
VerifiedBad,
}
-
/**
* Denomination record as stored in the wallet's database.
*/
@@ -640,7 +639,7 @@ export interface PlanchetRecord {
/**
* Public key of the reserve that this planchet
* is being withdrawn from.
- *
+ *
* Can be the empty string (non-null/undefined for DB indexing)
* if this is a tipping reserve.
*/
@@ -1532,7 +1531,6 @@ export enum ImportPayloadType {
CoreSchema = "core-schema",
}
-
class ExchangesStore extends Store<ExchangeRecord> {
constructor() {
super("exchanges", { keyPath: "baseUrl" });
@@ -1624,6 +1622,15 @@ class TipsStore extends Store<TipRecord> {
constructor() {
super("tips", { keyPath: "walletTipId" });
}
+ // Added in version 2
+ byMerchantTipIdAndBaseUrl = new Index<[string, string], TipRecord>(
+ this,
+ "tipsByMerchantTipIdAndOriginIndex",
+ ["merchantTipId", "merchantBaseUrl"],
+ {
+ versionAdded: 2,
+ }
+ );
}
class WithdrawalGroupsStore extends Store<WithdrawalGroupRecord> {
diff --git a/packages/taler-wallet-core/src/util/helpers.ts b/packages/taler-wallet-core/src/util/helpers.ts
index ae4b0359e..570df441d 100644
--- a/packages/taler-wallet-core/src/util/helpers.ts
+++ b/packages/taler-wallet-core/src/util/helpers.ts
@@ -146,3 +146,7 @@ export function strcmp(s1: string, s2: string): number {
}
return 0;
}
+
+export function j2s(x: any): string {
+ return JSON.stringify(x, undefined, 2);
+} \ No newline at end of file
diff --git a/packages/taler-wallet-core/src/util/query.ts b/packages/taler-wallet-core/src/util/query.ts
index 6ebc3bbc4..f533c4cfd 100644
--- a/packages/taler-wallet-core/src/util/query.ts
+++ b/packages/taler-wallet-core/src/util/query.ts
@@ -44,14 +44,25 @@ const logger = new Logger("query.ts");
*/
export const TransactionAbort = Symbol("transaction_abort");
+export interface StoreParams<T> {
+ validator?: (v: T) => T;
+ autoIncrement?: boolean;
+ keyPath?: string | string[] | null;
+
+ /**
+ * Database version that this store was added in, or
+ * undefined if added in the first version.
+ */
+ versionAdded?: number;
+}
+
/**
* Definition of an object store.
*/
export class Store<T> {
constructor(
public name: string,
- public storeParams?: IDBObjectStoreParameters,
- public validator?: (v: T) => T,
+ public storeParams?: StoreParams<T>,
) {}
}
@@ -66,6 +77,12 @@ export interface IndexOptions {
* Defaults to false.
*/
multiEntry?: boolean;
+
+ /**
+ * Database version that this store was added in, or
+ * undefined if added in the first version.
+ */
+ versionAdded?: number;
}
function requestToPromise(req: IDBRequest): Promise<any> {
@@ -425,6 +442,7 @@ export function openDatabase(
db: IDBDatabase,
oldVersion: number,
newVersion: number,
+ upgradeTransaction: IDBTransaction,
) => void,
): Promise<IDBDatabase> {
return new Promise<IDBDatabase>((resolve, reject) => {
@@ -449,7 +467,11 @@ export function openDatabase(
if (!newVersion) {
throw Error("upgrade needed, but new version unknown");
}
- onUpgradeNeeded(db, e.oldVersion, newVersion);
+ const transaction = req.transaction;
+ if (!transaction) {
+ throw Error("no transaction handle available in upgrade handler");
+ }
+ onUpgradeNeeded(db, e.oldVersion, newVersion, transaction);
};
});
}
diff --git a/packages/taler-wallet-core/src/util/taleruri.ts b/packages/taler-wallet-core/src/util/taleruri.ts
index 839d0b29f..ee055a32f 100644
--- a/packages/taler-wallet-core/src/util/taleruri.ts
+++ b/packages/taler-wallet-core/src/util/taleruri.ts
@@ -14,6 +14,7 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
+import { canonicalizeBaseUrl } from './helpers';
import { URLSearchParams } from "./url";
export interface PayUriResult {
@@ -59,7 +60,7 @@ export function parseWithdrawUri(s: string): WithdrawUriResult | undefined {
const p = [host, ...pathSegments].join("/");
return {
- bankIntegrationApiBaseUrl: `${pi.innerProto}://${p}/`,
+ bankIntegrationApiBaseUrl: canonicalizeBaseUrl(`${pi.innerProto}://${p}/`),
withdrawalOperationId: withdrawId,
};
}
@@ -155,7 +156,7 @@ export function parsePayUri(s: string): PayUriResult | undefined {
const orderId = parts[parts.length - 2];
const pathSegments = parts.slice(1, parts.length - 2);
const p = [host, ...pathSegments].join("/");
- const merchantBaseUrl = `${pi.innerProto}://${p}/`;
+ const merchantBaseUrl = canonicalizeBaseUrl(`${pi.innerProto}://${p}/`);
return {
merchantBaseUrl,
@@ -183,7 +184,7 @@ export function parseTipUri(s: string): TipUriResult | undefined {
const tipId = parts[parts.length - 1];
const pathSegments = parts.slice(1, parts.length - 1);
const p = [host, ...pathSegments].join("/");
- const merchantBaseUrl = `${pi.innerProto}://${p}/`;
+ const merchantBaseUrl = canonicalizeBaseUrl(`${pi.innerProto}://${p}/`);
return {
merchantBaseUrl,
@@ -210,7 +211,7 @@ export function parseRefundUri(s: string): RefundUriResult | undefined {
const orderId = parts[parts.length - 2];
const pathSegments = parts.slice(1, parts.length - 2);
const p = [host, ...pathSegments].join("/");
- const merchantBaseUrl = `${pi.innerProto}://${p}/`;
+ const merchantBaseUrl = canonicalizeBaseUrl(`${pi.innerProto}://${p}/`);
return {
merchantBaseUrl,
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f8b236858..5bca329ce 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -233,7 +233,7 @@ importers:
taler-wallet-core: 'workspace:*'
tslib: ^2.0.1
typescript: ^3.9.7
-lockfileVersion: 5.1
+lockfileVersion: 5.2
packages:
/@ava/typescript/1.1.1:
dependencies: