gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated: more static typing for transa


From: gnunet
Subject: [taler-wallet-core] branch master updated: more static typing for transactions (fixes #6653)
Date: Thu, 26 Nov 2020 22:14:55 +0100

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new 4e481a51 more static typing for transactions (fixes #6653)
4e481a51 is described below

commit 4e481a51c64084db21d3eea513b13a7a3bd6603a
Author: Florian Dold <florian@dold.me>
AuthorDate: Thu Nov 26 22:14:46 2020 +0100

    more static typing for transactions (fixes #6653)
---
 packages/taler-wallet-core/src/operations/tip.ts |   2 +-
 packages/taler-wallet-core/src/types/dbTypes.ts  | 130 ++++++++++++++---------
 packages/taler-wallet-core/src/util/query.ts     | 127 ++++++++++++++--------
 3 files changed, 164 insertions(+), 95 deletions(-)

diff --git a/packages/taler-wallet-core/src/operations/tip.ts 
b/packages/taler-wallet-core/src/operations/tip.ts
index cbf61a86..bf565b9b 100644
--- a/packages/taler-wallet-core/src/operations/tip.ts
+++ b/packages/taler-wallet-core/src/operations/tip.ts
@@ -285,7 +285,7 @@ async function processTipImpl(
     );
 
     if (!isValid) {
-      await ws.db.runWithWriteTransaction([Stores.planchets], async (tx) => {
+      await ws.db.runWithWriteTransaction([Stores.tips], async (tx) => {
         const tipRecord = await tx.get(Stores.tips, walletTipId);
         if (!tipRecord) {
           return;
diff --git a/packages/taler-wallet-core/src/types/dbTypes.ts 
b/packages/taler-wallet-core/src/types/dbTypes.ts
index ed3a18ae..349713eb 100644
--- a/packages/taler-wallet-core/src/types/dbTypes.ts
+++ b/packages/taler-wallet-core/src/types/dbTypes.ts
@@ -785,7 +785,7 @@ export interface CoinRecord {
 
   /**
    * Blinding key used when withdrawing the coin.
-   * Potentionally sed again during payback.
+   * Potentionally used again during payback.
    */
   blindingKey: string;
 
@@ -1531,135 +1531,160 @@ export enum ImportPayloadType {
   CoreSchema = "core-schema",
 }
 
-class ExchangesStore extends Store<ExchangeRecord> {
+class ExchangesStore extends Store<"exchanges", ExchangeRecord> {
   constructor() {
     super("exchanges", { keyPath: "baseUrl" });
   }
 }
 
-class CoinsStore extends Store<CoinRecord> {
+class CoinsStore extends Store<"coins", CoinRecord> {
   constructor() {
     super("coins", { keyPath: "coinPub" });
   }
 
-  exchangeBaseUrlIndex = new Index<string, CoinRecord>(
-    this,
-    "exchangeBaseUrl",
+  exchangeBaseUrlIndex = new Index<
+    "coins",
     "exchangeBaseUrl",
-  );
-  denomPubHashIndex = new Index<string, CoinRecord>(
-    this,
+    string,
+    CoinRecord
+  >(this, "exchangeBaseUrl", "exchangeBaseUrl");
+
+  denomPubHashIndex = new Index<
+    "coins",
     "denomPubHashIndex",
-    "denomPubHash",
-  );
+    string,
+    CoinRecord
+  >(this, "denomPubHashIndex", "denomPubHash");
 }
 
-class ProposalsStore extends Store<ProposalRecord> {
+class ProposalsStore extends Store<"proposals", ProposalRecord> {
   constructor() {
     super("proposals", { keyPath: "proposalId" });
   }
-  urlAndOrderIdIndex = new Index<string, ProposalRecord>(this, "urlIndex", [
-    "merchantBaseUrl",
-    "orderId",
-  ]);
+  urlAndOrderIdIndex = new Index<
+    "proposals",
+    "urlIndex",
+    string,
+    ProposalRecord
+  >(this, "urlIndex", ["merchantBaseUrl", "orderId"]);
 }
 
-class PurchasesStore extends Store<PurchaseRecord> {
+class PurchasesStore extends Store<"purchases", PurchaseRecord> {
   constructor() {
     super("purchases", { keyPath: "proposalId" });
   }
 
-  fulfillmentUrlIndex = new Index<string, PurchaseRecord>(
-    this,
+  fulfillmentUrlIndex = new Index<
+    "purchases",
     "fulfillmentUrlIndex",
-    "contractData.fulfillmentUrl",
+    string,
+    PurchaseRecord
+  >(this, "fulfillmentUrlIndex", "contractData.fulfillmentUrl");
+  
+  orderIdIndex = new Index<"purchases", "orderIdIndex", string, 
PurchaseRecord>(
+    this,
+    "orderIdIndex",
+    ["contractData.merchantBaseUrl", "contractData.orderId"],
   );
-  orderIdIndex = new Index<string, PurchaseRecord>(this, "orderIdIndex", [
-    "contractData.merchantBaseUrl",
-    "contractData.orderId",
-  ]);
 }
 
-class DenominationsStore extends Store<DenominationRecord> {
+class DenominationsStore extends Store<"denominations", DenominationRecord> {
   constructor() {
     // cast needed because of bug in type annotations
     super("denominations", {
       keyPath: (["exchangeBaseUrl", "denomPubHash"] as any) as IDBKeyPath,
     });
   }
-  exchangeBaseUrlIndex = new Index<string, DenominationRecord>(
-    this,
+  exchangeBaseUrlIndex = new Index<
+    "denominations",
     "exchangeBaseUrlIndex",
-    "exchangeBaseUrl",
-  );
+    string,
+    DenominationRecord
+  >(this, "exchangeBaseUrlIndex", "exchangeBaseUrl");
 }
 
-class CurrenciesStore extends Store<CurrencyRecord> {
+class CurrenciesStore extends Store<"currencies", CurrencyRecord> {
   constructor() {
     super("currencies", { keyPath: "name" });
   }
 }
 
-class ConfigStore extends Store<ConfigRecord> {
+class ConfigStore extends Store<"config", ConfigRecord> {
   constructor() {
     super("config", { keyPath: "key" });
   }
 }
 
-class ReservesStore extends Store<ReserveRecord> {
+class ReservesStore extends Store<"reserves", ReserveRecord> {
   constructor() {
     super("reserves", { keyPath: "reservePub" });
   }
 }
 
-class ReserveHistoryStore extends Store<ReserveHistoryRecord> {
+class ReserveHistoryStore extends Store<
+  "reserveHistory",
+  ReserveHistoryRecord
+> {
   constructor() {
     super("reserveHistory", { keyPath: "reservePub" });
   }
 }
 
-class TipsStore extends Store<TipRecord> {
+class TipsStore extends Store<"tips", TipRecord> {
   constructor() {
     super("tips", { keyPath: "walletTipId" });
   }
   // Added in version 2
-  byMerchantTipIdAndBaseUrl = new Index<[string, string], TipRecord>(
+  byMerchantTipIdAndBaseUrl = new Index<
+    "tips",
+    "tipsByMerchantTipIdAndOriginIndex",
+    [string, string],
+    TipRecord
+  >(
     this,
     "tipsByMerchantTipIdAndOriginIndex",
     ["merchantTipId", "merchantBaseUrl"],
     {
       versionAdded: 2,
-    }
+    },
   );
 }
 
-class WithdrawalGroupsStore extends Store<WithdrawalGroupRecord> {
+class WithdrawalGroupsStore extends Store<
+  "withdrawals",
+  WithdrawalGroupRecord
+> {
   constructor() {
     super("withdrawals", { keyPath: "withdrawalGroupId" });
   }
 }
 
-class PlanchetsStore extends Store<PlanchetRecord> {
+class PlanchetsStore extends Store<"planchets", PlanchetRecord> {
   constructor() {
     super("planchets", { keyPath: "coinPub" });
   }
-  byGroupAndIndex = new Index<string, PlanchetRecord>(
-    this,
+  byGroupAndIndex = new Index<
+    "planchets",
     "withdrawalGroupAndCoinIdxIndex",
-    ["withdrawalGroupId", "coinIdx"],
-  );
-  byGroup = new Index<string, PlanchetRecord>(
-    this,
+    string,
+    PlanchetRecord
+  >(this, "withdrawalGroupAndCoinIdxIndex", ["withdrawalGroupId", "coinIdx"]);
+  byGroup = new Index<
+    "planchets",
     "withdrawalGroupIndex",
-    "withdrawalGroupId",
-  );
+    string,
+    PlanchetRecord
+  >(this, "withdrawalGroupIndex", "withdrawalGroupId");
 }
 
 /**
  * This store is effectively a materialized index for
  * reserve records that are for a bank-integrated withdrawal.
  */
-class BankWithdrawUrisStore extends Store<BankWithdrawUriRecord> {
+class BankWithdrawUrisStore extends Store<
+  "bankWithdrawUris",
+  BankWithdrawUriRecord
+> {
   constructor() {
     super("bankWithdrawUris", { keyPath: "talerWithdrawUri" });
   }
@@ -1675,10 +1700,13 @@ export const Stores = {
   denominations: new DenominationsStore(),
   exchanges: new ExchangesStore(),
   proposals: new ProposalsStore(),
-  refreshGroups: new Store<RefreshGroupRecord>("refreshGroups", {
-    keyPath: "refreshGroupId",
-  }),
-  recoupGroups: new Store<RecoupGroupRecord>("recoupGroups", {
+  refreshGroups: new Store<"refreshGroups", RefreshGroupRecord>(
+    "refreshGroups",
+    {
+      keyPath: "refreshGroupId",
+    },
+  ),
+  recoupGroups: new Store<"recoupGroups", RecoupGroupRecord>("recoupGroups", {
     keyPath: "recoupGroupId",
   }),
   reserves: new ReservesStore(),
diff --git a/packages/taler-wallet-core/src/util/query.ts 
b/packages/taler-wallet-core/src/util/query.ts
index f533c4cf..fa0d8beb 100644
--- a/packages/taler-wallet-core/src/util/query.ts
+++ b/packages/taler-wallet-core/src/util/query.ts
@@ -59,11 +59,8 @@ export interface StoreParams<T> {
 /**
  * Definition of an object store.
  */
-export class Store<T> {
-  constructor(
-    public name: string,
-    public storeParams?: StoreParams<T>,
-  ) {}
+export class Store<N extends string, T> {
+  constructor(public name: N, public storeParams?: StoreParams<T>) {}
 }
 
 /**
@@ -273,26 +270,48 @@ class ResultStream<T> {
   }
 }
 
-export class TransactionHandle {
+type StrKey<T> = string & keyof T;
+
+type StoreName<S> = S extends Store<infer N, any> ? N : 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;
+
+export class TransactionHandle<StoreTypes extends Store<string, {}>> {
   constructor(private tx: IDBTransaction) {}
 
-  put<T>(store: Store<T>, value: T, key?: any): Promise<any> {
+  put<S extends StoreTypes>(
+    store: S,
+    value: StoreContent<S>,
+    key?: any,
+  ): Promise<any> {
     const req = this.tx.objectStore(store.name).put(value, key);
     return requestToPromise(req);
   }
 
-  add<T>(store: Store<T>, value: T, key?: any): Promise<any> {
+  add<S extends StoreTypes>(
+    store: S,
+    value: StoreContent<S>,
+    key?: any,
+  ): Promise<any> {
     const req = this.tx.objectStore(store.name).add(value, key);
     return requestToPromise(req);
   }
 
-  get<T>(store: Store<T>, key: any): Promise<T | undefined> {
+  get<S extends StoreTypes>(
+    store: S,
+    key: any,
+  ): Promise<StoreContent<S> | undefined> {
     const req = this.tx.objectStore(store.name).get(key);
     return requestToPromise(req);
   }
 
-  getIndexed<S extends IDBValidKey, T>(
-    index: Index<S, T>,
+  getIndexed<
+    StoreName extends StrKey<StoreTypes>,
+    IndexName extends string,
+    S extends IDBValidKey,
+    T
+  >(
+    index: Index<StoreName, IndexName, S, T>,
     key: any,
   ): Promise<T | undefined> {
     const req = this.tx
@@ -302,15 +321,20 @@ export class TransactionHandle {
     return requestToPromise(req);
   }
 
-  iter<T>(store: Store<T>, key?: any): ResultStream<T> {
+  iter<N extends StrKey<StoreTypes>, T extends StoreTypes[N]>(
+    store: Store<N, T>,
+    key?: any,
+  ): ResultStream<T> {
     const req = this.tx.objectStore(store.name).openCursor(key);
     return new ResultStream<T>(req);
   }
 
-  iterIndexed<S extends IDBValidKey, T>(
-    index: Index<S, T>,
-    key?: any,
-  ): ResultStream<T> {
+  iterIndexed<
+    StoreName extends StrKey<StoreTypes>,
+    IndexName extends string,
+    S extends IDBValidKey,
+    T
+  >(index: Index<StoreName, IndexName, S, T>, key?: any): ResultStream<T> {
     const req = this.tx
       .objectStore(index.storeName)
       .index(index.indexName)
@@ -318,13 +342,16 @@ export class TransactionHandle {
     return new ResultStream<T>(req);
   }
 
-  delete<T>(store: Store<T>, key: any): Promise<void> {
+  delete<N extends StrKey<StoreTypes>, T extends StoreTypes[N]>(
+    store: Store<N, T>,
+    key: any,
+  ): Promise<void> {
     const req = this.tx.objectStore(store.name).delete(key);
     return requestToPromise(req);
   }
 
-  mutate<T>(
-    store: Store<T>,
+  mutate<N extends StrKey<StoreTypes>, T extends StoreTypes[N]>(
+    store: Store<N, T>,
     key: any,
     f: (x: T) => T | undefined,
   ): Promise<void> {
@@ -333,10 +360,10 @@ export class TransactionHandle {
   }
 }
 
-function runWithTransaction<T>(
+function runWithTransaction<T, StoreTypes extends Store<string, {}>>(
   db: IDBDatabase,
-  stores: Store<any>[],
-  f: (t: TransactionHandle) => Promise<T>,
+  stores: StoreTypes[],
+  f: (t: TransactionHandle<StoreTypes>) => Promise<T>,
   mode: "readonly" | "readwrite",
 ): Promise<T> {
   const stack = Error("Failed transaction was started here.");
@@ -397,7 +424,12 @@ function runWithTransaction<T>(
 /**
  * Definition of an index.
  */
-export class Index<S extends IDBValidKey, T> {
+export class Index<
+  StoreName extends string,
+  IndexName extends string,
+  S extends IDBValidKey,
+  T
+> {
   /**
    * Name of the store that this index is associated with.
    */
@@ -409,8 +441,8 @@ export class Index<S extends IDBValidKey, T> {
   options: IndexOptions;
 
   constructor(
-    s: Store<T>,
-    public indexName: string,
+    s: Store<StoreName, T>,
+    public indexName: IndexName,
     public keyPath: string | string[],
     options?: IndexOptions,
   ) {
@@ -539,7 +571,10 @@ export class Database {
     });
   }
 
-  async get<T>(store: Store<T>, key: any): Promise<T | undefined> {
+  async get<N extends string, T>(
+    store: Store<N, T>,
+    key: any,
+  ): Promise<T | undefined> {
     const tx = this.db.transaction([store.name], "readonly");
     const req = tx.objectStore(store.name).get(key);
     const v = await requestToPromise(req);
@@ -547,10 +582,12 @@ export class Database {
     return v;
   }
 
-  async getIndexed<S extends IDBValidKey, T>(
-    index: Index<S, T>,
+  async getIndexed<Ind extends Index<string, string, any, any>>(
+    index: Ind extends Index<infer IndN, infer StN, any, infer R>
+      ? Index<IndN, StN, any, R>
+      : never,
     key: any,
-  ): Promise<T | undefined> {
+  ): Promise<IndexRecord<Ind> | undefined> {
     const tx = this.db.transaction([index.storeName], "readonly");
     const req = 
tx.objectStore(index.storeName).index(index.indexName).get(key);
     const v = await requestToPromise(req);
@@ -558,7 +595,11 @@ export class Database {
     return v;
   }
 
-  async put<T>(store: Store<T>, value: T, key?: any): Promise<any> {
+  async put<St extends Store<string, any>>(
+    store: St extends Store<infer N, infer R> ? Store<N, R> : never,
+    value: St extends Store<any, infer R> ? R : never,
+    key?: any,
+  ): Promise<any> {
     const tx = this.db.transaction([store.name], "readwrite");
     const req = tx.objectStore(store.name).put(value, key);
     const v = await requestToPromise(req);
@@ -566,8 +607,8 @@ export class Database {
     return v;
   }
 
-  async mutate<T>(
-    store: Store<T>,
+  async mutate<N extends string, T>(
+    store: Store<N, T>,
     key: any,
     f: (x: T) => T | undefined,
   ): Promise<void> {
@@ -577,14 +618,14 @@ export class Database {
     await transactionToPromise(tx);
   }
 
-  iter<T>(store: Store<T>): ResultStream<T> {
+  iter<N extends string, T>(store: Store<N, T>): ResultStream<T> {
     const tx = this.db.transaction([store.name], "readonly");
     const req = tx.objectStore(store.name).openCursor();
     return new ResultStream<T>(req);
   }
 
-  iterIndex<S extends IDBValidKey, T>(
-    index: Index<S, T>,
+  iterIndex<N extends string, I extends string, S extends IDBValidKey, T>(
+    index: Index<N, I, S, T>,
     query?: any,
   ): ResultStream<T> {
     const tx = this.db.transaction([index.storeName], "readonly");
@@ -595,17 +636,17 @@ export class Database {
     return new ResultStream<T>(req);
   }
 
-  async runWithReadTransaction<T>(
-    stores: Store<any>[],
-    f: (t: TransactionHandle) => Promise<T>,
+  async runWithReadTransaction<T, StoreTypes extends Store<string, any>>(
+    stores: StoreTypes[],
+    f: (t: TransactionHandle<StoreTypes>) => Promise<T>,
   ): Promise<T> {
-    return runWithTransaction<T>(this.db, stores, f, "readonly");
+    return runWithTransaction<T, StoreTypes>(this.db, stores, f, "readonly");
   }
 
-  async runWithWriteTransaction<T>(
-    stores: Store<any>[],
-    f: (t: TransactionHandle) => Promise<T>,
+  async runWithWriteTransaction<T, StoreTypes extends Store<string, any>>(
+    stores: StoreTypes[],
+    f: (t: TransactionHandle<StoreTypes>) => Promise<T>,
   ): Promise<T> {
-    return runWithTransaction<T>(this.db, stores, f, "readwrite");
+    return runWithTransaction<T, StoreTypes>(this.db, stores, f, "readwrite");
   }
 }

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]