commit c39124414fa013a6bd42ee48c646775429776aa5
parent 8027c7c743e0e7b5d117808c7dd50bde50ee2465
Author: Florian Dold <dold@taler.net>
Date: Wed, 19 Aug 2026 21:42:01 +0200
wallet-core: batch purchase and token lookups
Diffstat:
1 file changed, 53 insertions(+), 76 deletions(-)
diff --git a/packages/taler-wallet-core/src/tokenFamilies.ts b/packages/taler-wallet-core/src/tokenFamilies.ts
@@ -45,45 +45,31 @@ function groupDiscounts(
tokens: WalletToken[],
merchantInfos: MerchantInfos,
): DiscountListDetail[] {
- const groupedIdx: number[] = [];
- const items: DiscountListDetail[] = [];
- tokens = tokens
+ const grouped = new Map<string, DiscountListDetail>();
+ const sortedTokens = tokens
.filter((t) => t.tokenFamilyHash)
.sort((a, b) => a.validBefore - b.validBefore);
-
- // compare tokens against each other,
- // except the ones already in a group.
- for (let a = 0; a < tokens.length; a++) {
- if (groupedIdx.includes(a)) continue;
- let tokenA = tokens[a];
- const item: DiscountListDetail = {
- tokenFamilyHash: tokenA.tokenFamilyHash!,
- tokenIssuePubHash: tokenA.tokenIssuePubHash,
- merchantBaseUrl: tokenA.merchantBaseUrl,
- merchantInfo: merchantInfos[tokenA.purchaseId],
- name: tokenA.name,
- description: tokenA.description,
- descriptionI18n: tokenA.descriptionI18n,
- validityStart: tokenA.tokenIssuePub.signature_validity_start,
- validityEnd: tokenA.tokenIssuePub.signature_validity_end,
- tokensAvailable: 1,
- };
-
- for (let b = 0; b < tokens.length; b++) {
- if (b === a) continue;
- if (groupedIdx.includes(b)) continue;
- let tokenB = tokens[b];
- if (tokenA.tokenFamilyHash === tokenB.tokenFamilyHash) {
- item.tokensAvailable += 1;
- groupedIdx.push(b);
- }
+ for (const token of sortedTokens) {
+ const familyHash = token.tokenFamilyHash!;
+ const existing = grouped.get(familyHash);
+ if (existing) {
+ existing.tokensAvailable++;
+ continue;
}
-
- groupedIdx.push(a);
- items.push(item);
+ grouped.set(familyHash, {
+ tokenFamilyHash: familyHash,
+ tokenIssuePubHash: token.tokenIssuePubHash,
+ merchantBaseUrl: token.merchantBaseUrl,
+ merchantInfo: merchantInfos[token.purchaseId],
+ name: token.name,
+ description: token.description,
+ descriptionI18n: token.descriptionI18n,
+ validityStart: token.tokenIssuePub.signature_validity_start,
+ validityEnd: token.tokenIssuePub.signature_validity_end,
+ tokensAvailable: 1,
+ });
}
-
- return items;
+ return [...grouped.values()];
}
// FIXME: unit test for subscription grouping
@@ -91,43 +77,29 @@ function groupSubscriptions(
tokens: WalletToken[],
merchantInfos: MerchantInfos,
): SubscriptionListDetail[] {
- const groupedIdx: number[] = [];
- const items: SubscriptionListDetail[] = [];
- tokens = tokens
+ const grouped = new Map<string, SubscriptionListDetail>();
+ const sortedTokens = tokens
.filter((t) => t.tokenFamilyHash)
.sort((a, b) => a.validBefore - b.validBefore);
- // compare tokens against each other,
- // except the ones already in a group.
- for (let a = 0; a < tokens.length; a++) {
- if (groupedIdx.includes(a)) continue;
- let tokenA = tokens[a];
- const item: SubscriptionListDetail = {
- tokenFamilyHash: tokenA.tokenFamilyHash!,
- tokenIssuePubHash: tokenA.tokenIssuePubHash,
- merchantBaseUrl: tokenA.merchantBaseUrl,
- merchantInfo: merchantInfos[tokenA.purchaseId],
- name: tokenA.name,
- description: tokenA.description,
- descriptionI18n: tokenA.descriptionI18n,
- validityStart: tokenA.tokenIssuePub.signature_validity_start,
- validityEnd: tokenA.tokenIssuePub.signature_validity_end,
- };
-
- for (let b = 0; b < tokens.length; b++) {
- if (b === a) continue;
- if (groupedIdx.includes(b)) continue;
- let tokenB = tokens[b];
- if (tokenA.tokenFamilyHash === tokenB.tokenFamilyHash) {
- groupedIdx.push(b);
- }
+ for (const token of sortedTokens) {
+ const familyHash = token.tokenFamilyHash!;
+ if (grouped.has(familyHash)) {
+ continue;
}
-
- groupedIdx.push(a);
- items.push(item);
+ grouped.set(familyHash, {
+ tokenFamilyHash: familyHash,
+ tokenIssuePubHash: token.tokenIssuePubHash,
+ merchantBaseUrl: token.merchantBaseUrl,
+ merchantInfo: merchantInfos[token.purchaseId],
+ name: token.name,
+ description: token.description,
+ descriptionI18n: token.descriptionI18n,
+ validityStart: token.tokenIssuePub.signature_validity_start,
+ validityEnd: token.tokenIssuePub.signature_validity_end,
+ });
}
-
- return items;
+ return [...grouped.values()];
}
async function getMerchantInfos(
@@ -136,13 +108,12 @@ async function getMerchantInfos(
): Promise<MerchantInfos> {
const merchantInfos: MerchantInfos = {};
await wex.runWalletDbTx(async (tx) => {
- for (const t of tokens) {
- if (t.purchaseId in merchantInfos) continue;
- const purchase = await tx.getPurchase(t.purchaseId);
- if (!purchase) continue;
+ const purchaseIds = [...new Set(tokens.map((t) => t.purchaseId))];
+ const purchases = await tx.getPurchasesByIds(purchaseIds);
+ for (const purchase of purchases) {
const download = await expectProposalDownloadInTx(wex, tx, purchase);
const contractData = download.contractTerms;
- merchantInfos[t.purchaseId] = contractData.merchant;
+ merchantInfos[purchase.proposalId] = contractData.merchant;
}
});
return merchantInfos;
@@ -154,7 +125,10 @@ export async function listDiscounts(
merchantBaseUrl?: string,
): Promise<ListDiscountsResponse> {
const tokens: WalletToken[] = await wex.runWalletDbTx(async (tx) => {
- return (await tx.listTokens())
+ const candidates = tokenIssuePubHash
+ ? await tx.getTokensByIssuePubHash(tokenIssuePubHash)
+ : await tx.listTokens();
+ return candidates
.filter((t) => isTokenValid(t))
.filter((t) => t.kind === MerchantContractTokenKind.Discount)
.filter(
@@ -178,7 +152,10 @@ export async function listSubscriptions(
merchantBaseUrl?: string,
): Promise<ListSubscriptionsResponse> {
const tokens: WalletToken[] = await wex.runWalletDbTx(async (tx) => {
- return (await tx.listTokens())
+ const candidates = tokenIssuePubHash
+ ? await tx.getTokensByIssuePubHash(tokenIssuePubHash)
+ : await tx.listTokens();
+ return candidates
.filter((t) => isTokenValid(t))
.filter((t) => t.kind === MerchantContractTokenKind.Subscription)
.filter(
@@ -204,7 +181,7 @@ export async function deleteDiscount(
tokenFamilyHash: string,
): Promise<EmptyObject> {
await wex.runWalletDbTx(async (tx) => {
- const tokens = (await tx.listTokens())
+ const tokens = (await tx.getTokensByFamilyHash(tokenFamilyHash))
.filter((t) => t.kind === MerchantContractTokenKind.Discount)
.filter((t) => t.tokenFamilyHash === tokenFamilyHash);
@@ -242,7 +219,7 @@ export async function deleteSubscription(
tokenFamilyHash: string,
): Promise<EmptyObject> {
await wex.runWalletDbTx(async (tx) => {
- const tokens = (await tx.listTokens())
+ const tokens = (await tx.getTokensByFamilyHash(tokenFamilyHash))
.filter((t) => t.kind === MerchantContractTokenKind.Subscription)
.filter((t) => t.tokenFamilyHash === tokenFamilyHash)
.sort((a, b) => a.validBefore - b.validBefore);