From 48540f62644b4c2e4e96095b11e202cb62e3e93d Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Tue, 13 Sep 2022 13:25:41 +0200 Subject: wallet-core: introduce easier syntax for transactions --- packages/taler-wallet-core/src/util/query.ts | 94 ++++++++++++++------------ packages/taler-wallet-core/src/util/retries.ts | 3 +- 2 files changed, 51 insertions(+), 46 deletions(-) (limited to 'packages/taler-wallet-core/src/util') diff --git a/packages/taler-wallet-core/src/util/query.ts b/packages/taler-wallet-core/src/util/query.ts index 65b67eff2..025959253 100644 --- a/packages/taler-wallet-core/src/util/query.ts +++ b/packages/taler-wallet-core/src/util/query.ts @@ -36,6 +36,7 @@ import { } from "@gnu-taler/idb-bridge"; import { Logger } from "@gnu-taler/taler-util"; import { performanceNow } from "./timer.js"; +import { access } from "fs"; const logger = new Logger("query.ts"); @@ -280,7 +281,6 @@ export interface IndexDescriptor { export interface StoreDescriptor { _dummy: undefined & RecordType; - name: string; keyPath?: IDBKeyPath | IDBKeyPath[]; autoIncrement?: boolean; } @@ -291,10 +291,9 @@ export interface StoreOptions { } export function describeContents( - name: string, options: StoreOptions, ): StoreDescriptor { - return { name, keyPath: options.keyPath, _dummy: undefined as any }; + return { keyPath: options.keyPath, _dummy: undefined as any }; } export function describeIndex( @@ -345,9 +344,11 @@ export interface StoreReadWriteAccessor { } export interface StoreWithIndexes< + StoreName extends string, SD extends StoreDescriptor, IndexMap, > { + storeName: StoreName; store: SD; indexMap: IndexMap; @@ -362,11 +363,17 @@ export type GetRecordType = T extends StoreDescriptor ? X : unknown; const storeWithIndexesSymbol = Symbol("StoreWithIndexesMark"); -export function describeStore, IndexMap>( +export function describeStore< + StoreName extends string, + SD extends StoreDescriptor, + IndexMap, +>( + name: StoreName, s: SD, m: IndexMap, -): StoreWithIndexes { +): StoreWithIndexes { return { + storeName: name, store: s, indexMap: m, mark: storeWithIndexesSymbol, @@ -375,6 +382,7 @@ export function describeStore, IndexMap>( export type GetReadOnlyAccess = { [P in keyof BoundStores]: BoundStores[P] extends StoreWithIndexes< + infer SN, infer SD, infer IM > @@ -384,6 +392,7 @@ export type GetReadOnlyAccess = { export type GetReadWriteAccess = { [P in keyof BoundStores]: BoundStores[P] extends StoreWithIndexes< + infer SN, infer SD, infer IM > @@ -404,8 +413,12 @@ export interface TransactionContext { runReadOnly(f: ReadOnlyTransactionFunction): Promise; } -type CheckDescriptor = T extends StoreWithIndexes - ? StoreWithIndexes +type CheckDescriptor = T extends StoreWithIndexes< + infer SN, + infer SD, + infer IM +> + ? StoreWithIndexes : unknown; type GetPickerType = F extends (x: SM) => infer Out @@ -477,13 +490,13 @@ function runTx( function makeReadContext( tx: IDBTransaction, - storePick: { [n: string]: StoreWithIndexes }, + storePick: { [n: string]: StoreWithIndexes }, ): any { const ctx: { [s: string]: StoreReadOnlyAccessor } = {}; for (const storeAlias in storePick) { const indexes: { [s: string]: IndexReadOnlyAccessor } = {}; const swi = storePick[storeAlias]; - const storeName = swi.store.name; + const storeName = swi.storeName; for (const indexAlias in storePick[storeAlias].indexMap) { const indexDescriptor: IndexDescriptor = storePick[storeAlias].indexMap[indexAlias]; @@ -526,13 +539,13 @@ function makeReadContext( function makeWriteContext( tx: IDBTransaction, - storePick: { [n: string]: StoreWithIndexes }, + storePick: { [n: string]: StoreWithIndexes }, ): any { const ctx: { [s: string]: StoreReadWriteAccessor } = {}; for (const storeAlias in storePick) { const indexes: { [s: string]: IndexReadWriteAccessor } = {}; const swi = storePick[storeAlias]; - const storeName = swi.store.name; + const storeName = swi.storeName; for (const indexAlias in storePick[storeAlias].indexMap) { const indexDescriptor: IndexDescriptor = storePick[storeAlias].indexMap[indexAlias]; @@ -585,25 +598,11 @@ function makeWriteContext( return ctx; } -const storeList = [ - { name: "foo" as const, value: 1 as const }, - { name: "bar" as const, value: 2 as const }, -]; -// => { foo: { value: 1}, bar: {value: 2} } - -type StoreList = typeof storeList; - -type StoreNames = StoreList[number] extends { name: infer I } ? I : never; - -type H = StoreList[number] & { name: "foo"}; - -type Cleanup = V extends { name: infer N, value: infer X} ? {name: N, value: X} : never; - -type G = { - [X in StoreNames]: { - X: StoreList[number] & { name: X }; - }; -}; +type StoreNamesOf = X extends { [x: number]: infer F } + ? F extends { storeName: infer I } + ? I + : never + : never; /** * Type-safe access to a database with a particular store map. @@ -617,36 +616,41 @@ export class DbAccess { return this.db; } - mktx2< + /** + * Run a transaction with selected object stores. + * + * The {@link namePicker} must be a function that selects a list of object + * stores from all available object stores. + */ + mktx< StoreNames extends keyof StoreMap, Stores extends StoreMap[StoreNames], StoreList extends Stores[], - >(namePicker: (x: StoreMap) => StoreList): StoreList { - return namePicker(this.stores); - } - - mktx< - PickerType extends (x: StoreMap) => unknown, - BoundStores extends GetPickerType, - >(f: PickerType): TransactionContext { - const storePick = f(this.stores) as any; + BoundStores extends { + [X in StoreNamesOf]: StoreList[number] & { storeName: X }; + }, + >(namePicker: (x: StoreMap) => StoreList): TransactionContext { + const storePick = namePicker(this.stores) as any; if (typeof storePick !== "object" || storePick === null) { throw Error(); } const storeNames: string[] = []; - for (const storeAlias of Object.keys(storePick)) { - const swi = (storePick as any)[storeAlias] as StoreWithIndexes; + const accessibleStores: { [x: string]: StoreWithIndexes } = + {}; + for (const swiPicked of storePick) { + const swi = swiPicked as StoreWithIndexes; if (swi.mark !== storeWithIndexesSymbol) { throw Error("invalid store descriptor returned from selector function"); } - storeNames.push(swi.store.name); + storeNames.push(swi.storeName); + accessibleStores[swi.storeName] = swi; } const runReadOnly = ( txf: ReadOnlyTransactionFunction, ): Promise => { const tx = this.db.transaction(storeNames, "readonly"); - const readContext = makeReadContext(tx, storePick); + const readContext = makeReadContext(tx, accessibleStores); return runTx(tx, readContext, txf); }; @@ -654,7 +658,7 @@ export class DbAccess { txf: ReadWriteTransactionFunction, ): Promise => { const tx = this.db.transaction(storeNames, "readwrite"); - const writeContext = makeWriteContext(tx, storePick); + const writeContext = makeWriteContext(tx, accessibleStores); return runTx(tx, writeContext, txf); }; diff --git a/packages/taler-wallet-core/src/util/retries.ts b/packages/taler-wallet-core/src/util/retries.ts index 3a41e8348..4763bbc42 100644 --- a/packages/taler-wallet-core/src/util/retries.ts +++ b/packages/taler-wallet-core/src/util/retries.ts @@ -201,8 +201,9 @@ export async function scheduleRetry( errorDetail?: TalerErrorDetail, ): Promise { return await ws.db - .mktx((x) => ({ operationRetries: x.operationRetries })) + .mktx((x) => [x.operationRetries]) .runReadWrite(async (tx) => { + tx.operationRetries scheduleRetryInTx(ws, tx, opId, errorDetail); }); } -- cgit v1.2.3