fix tip record creation, migrate DB
This commit is contained in:
parent
cdf5cc583c
commit
292160f7e9
@ -1,6 +1,7 @@
|
|||||||
import { Stores } from "./types/dbTypes";
|
import { Stores } from "./types/dbTypes";
|
||||||
import { openDatabase, Database, Store, Index } from "./util/query";
|
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
|
* 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
|
* backwards-compatible way or object stores and indices
|
||||||
* are added.
|
* 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
|
* Return a promise that resolves
|
||||||
@ -31,9 +34,9 @@ export function openTalerDatabase(
|
|||||||
db: IDBDatabase,
|
db: IDBDatabase,
|
||||||
oldVersion: number,
|
oldVersion: number,
|
||||||
newVersion: number,
|
newVersion: number,
|
||||||
|
upgradeTransaction: IDBTransaction,
|
||||||
): void => {
|
): void => {
|
||||||
switch (oldVersion) {
|
if (oldVersion === 0) {
|
||||||
case 0: // DB does not exist yet
|
|
||||||
for (const n in Stores) {
|
for (const n in Stores) {
|
||||||
if ((Stores as any)[n] instanceof Store) {
|
if ((Stores as any)[n] instanceof Store) {
|
||||||
const si: Store<any> = (Stores as any)[n];
|
const si: Store<any> = (Stores as any)[n];
|
||||||
@ -46,9 +49,30 @@ export function openTalerDatabase(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
return;
|
||||||
default:
|
}
|
||||||
throw Error("unsupported existig DB version");
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@ import { Logger } from "../util/logging";
|
|||||||
import { checkDbInvariant } from "../util/invariants";
|
import { checkDbInvariant } from "../util/invariants";
|
||||||
import { TalerErrorCode } from "../TalerErrorCode";
|
import { TalerErrorCode } from "../TalerErrorCode";
|
||||||
import { initRetryInfo, updateRetryInfoTimeout } from "../util/retries";
|
import { initRetryInfo, updateRetryInfoTimeout } from "../util/retries";
|
||||||
|
import { j2s } from '../util/helpers';
|
||||||
|
|
||||||
const logger = new Logger("operations/tip.ts");
|
const logger = new Logger("operations/tip.ts");
|
||||||
|
|
||||||
@ -68,7 +69,7 @@ export async function prepareTip(
|
|||||||
merchantResp,
|
merchantResp,
|
||||||
codecForTipPickupGetResponse(),
|
codecForTipPickupGetResponse(),
|
||||||
);
|
);
|
||||||
logger.trace(`status ${tipPickupStatus}`);
|
logger.trace(`status ${j2s(tipPickupStatus)}`);
|
||||||
|
|
||||||
const amount = Amounts.parseOrThrow(tipPickupStatus.tip_amount);
|
const amount = Amounts.parseOrThrow(tipPickupStatus.tip_amount);
|
||||||
|
|
||||||
@ -80,6 +81,7 @@ export async function prepareTip(
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
if (!tipRecord) {
|
if (!tipRecord) {
|
||||||
|
logger.trace("new tip, creating tip record");
|
||||||
await updateExchangeFromUrl(ws, tipPickupStatus.exchange_url);
|
await updateExchangeFromUrl(ws, tipPickupStatus.exchange_url);
|
||||||
const withdrawDetails = await getExchangeWithdrawalInfo(
|
const withdrawDetails = await getExchangeWithdrawalInfo(
|
||||||
ws,
|
ws,
|
||||||
@ -232,7 +234,7 @@ async function processTipImpl(
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const tipStatusUrl = new URL(
|
const tipStatusUrl = new URL(
|
||||||
`/tips/${tipRecord.merchantTipId}/pickup`,
|
`tips/${tipRecord.merchantTipId}/pickup`,
|
||||||
tipRecord.merchantBaseUrl,
|
tipRecord.merchantBaseUrl,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -361,7 +361,6 @@ export enum DenominationStatus {
|
|||||||
VerifiedBad,
|
VerifiedBad,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Denomination record as stored in the wallet's database.
|
* Denomination record as stored in the wallet's database.
|
||||||
*/
|
*/
|
||||||
@ -1532,7 +1531,6 @@ export enum ImportPayloadType {
|
|||||||
CoreSchema = "core-schema",
|
CoreSchema = "core-schema",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ExchangesStore extends Store<ExchangeRecord> {
|
class ExchangesStore extends Store<ExchangeRecord> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("exchanges", { keyPath: "baseUrl" });
|
super("exchanges", { keyPath: "baseUrl" });
|
||||||
@ -1624,6 +1622,15 @@ class TipsStore extends Store<TipRecord> {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super("tips", { keyPath: "walletTipId" });
|
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> {
|
class WithdrawalGroupsStore extends Store<WithdrawalGroupRecord> {
|
||||||
|
@ -146,3 +146,7 @@ export function strcmp(s1: string, s2: string): number {
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function j2s(x: any): string {
|
||||||
|
return JSON.stringify(x, undefined, 2);
|
||||||
|
}
|
@ -44,14 +44,25 @@ const logger = new Logger("query.ts");
|
|||||||
*/
|
*/
|
||||||
export const TransactionAbort = Symbol("transaction_abort");
|
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.
|
* Definition of an object store.
|
||||||
*/
|
*/
|
||||||
export class Store<T> {
|
export class Store<T> {
|
||||||
constructor(
|
constructor(
|
||||||
public name: string,
|
public name: string,
|
||||||
public storeParams?: IDBObjectStoreParameters,
|
public storeParams?: StoreParams<T>,
|
||||||
public validator?: (v: T) => T,
|
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,6 +77,12 @@ export interface IndexOptions {
|
|||||||
* Defaults to false.
|
* Defaults to false.
|
||||||
*/
|
*/
|
||||||
multiEntry?: boolean;
|
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> {
|
function requestToPromise(req: IDBRequest): Promise<any> {
|
||||||
@ -425,6 +442,7 @@ export function openDatabase(
|
|||||||
db: IDBDatabase,
|
db: IDBDatabase,
|
||||||
oldVersion: number,
|
oldVersion: number,
|
||||||
newVersion: number,
|
newVersion: number,
|
||||||
|
upgradeTransaction: IDBTransaction,
|
||||||
) => void,
|
) => void,
|
||||||
): Promise<IDBDatabase> {
|
): Promise<IDBDatabase> {
|
||||||
return new Promise<IDBDatabase>((resolve, reject) => {
|
return new Promise<IDBDatabase>((resolve, reject) => {
|
||||||
@ -449,7 +467,11 @@ export function openDatabase(
|
|||||||
if (!newVersion) {
|
if (!newVersion) {
|
||||||
throw Error("upgrade needed, but new version unknown");
|
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);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { canonicalizeBaseUrl } from './helpers';
|
||||||
import { URLSearchParams } from "./url";
|
import { URLSearchParams } from "./url";
|
||||||
|
|
||||||
export interface PayUriResult {
|
export interface PayUriResult {
|
||||||
@ -59,7 +60,7 @@ export function parseWithdrawUri(s: string): WithdrawUriResult | undefined {
|
|||||||
const p = [host, ...pathSegments].join("/");
|
const p = [host, ...pathSegments].join("/");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
bankIntegrationApiBaseUrl: `${pi.innerProto}://${p}/`,
|
bankIntegrationApiBaseUrl: canonicalizeBaseUrl(`${pi.innerProto}://${p}/`),
|
||||||
withdrawalOperationId: withdrawId,
|
withdrawalOperationId: withdrawId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -155,7 +156,7 @@ export function parsePayUri(s: string): PayUriResult | undefined {
|
|||||||
const orderId = parts[parts.length - 2];
|
const orderId = parts[parts.length - 2];
|
||||||
const pathSegments = parts.slice(1, parts.length - 2);
|
const pathSegments = parts.slice(1, parts.length - 2);
|
||||||
const p = [host, ...pathSegments].join("/");
|
const p = [host, ...pathSegments].join("/");
|
||||||
const merchantBaseUrl = `${pi.innerProto}://${p}/`;
|
const merchantBaseUrl = canonicalizeBaseUrl(`${pi.innerProto}://${p}/`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
merchantBaseUrl,
|
merchantBaseUrl,
|
||||||
@ -183,7 +184,7 @@ export function parseTipUri(s: string): TipUriResult | undefined {
|
|||||||
const tipId = parts[parts.length - 1];
|
const tipId = parts[parts.length - 1];
|
||||||
const pathSegments = parts.slice(1, parts.length - 1);
|
const pathSegments = parts.slice(1, parts.length - 1);
|
||||||
const p = [host, ...pathSegments].join("/");
|
const p = [host, ...pathSegments].join("/");
|
||||||
const merchantBaseUrl = `${pi.innerProto}://${p}/`;
|
const merchantBaseUrl = canonicalizeBaseUrl(`${pi.innerProto}://${p}/`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
merchantBaseUrl,
|
merchantBaseUrl,
|
||||||
@ -210,7 +211,7 @@ export function parseRefundUri(s: string): RefundUriResult | undefined {
|
|||||||
const orderId = parts[parts.length - 2];
|
const orderId = parts[parts.length - 2];
|
||||||
const pathSegments = parts.slice(1, parts.length - 2);
|
const pathSegments = parts.slice(1, parts.length - 2);
|
||||||
const p = [host, ...pathSegments].join("/");
|
const p = [host, ...pathSegments].join("/");
|
||||||
const merchantBaseUrl = `${pi.innerProto}://${p}/`;
|
const merchantBaseUrl = canonicalizeBaseUrl(`${pi.innerProto}://${p}/`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
merchantBaseUrl,
|
merchantBaseUrl,
|
||||||
|
@ -233,7 +233,7 @@ importers:
|
|||||||
taler-wallet-core: 'workspace:*'
|
taler-wallet-core: 'workspace:*'
|
||||||
tslib: ^2.0.1
|
tslib: ^2.0.1
|
||||||
typescript: ^3.9.7
|
typescript: ^3.9.7
|
||||||
lockfileVersion: 5.1
|
lockfileVersion: 5.2
|
||||||
packages:
|
packages:
|
||||||
/@ava/typescript/1.1.1:
|
/@ava/typescript/1.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
Loading…
Reference in New Issue
Block a user