summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/util
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2023-06-20 11:40:06 +0200
committerFlorian Dold <florian@dold.me>2023-06-20 11:40:06 +0200
commit9c708251f92e6691ebba80fa8d129c6c04cec618 (patch)
treeedf46c7b3f9386697a4ea697c2d66f66323a6d3e /packages/taler-wallet-core/src/util
parent54f0c82999833132baf83995526025ac56d6fe06 (diff)
downloadwallet-core-9c708251f92e6691ebba80fa8d129c6c04cec618.tar.gz
wallet-core-9c708251f92e6691ebba80fa8d129c6c04cec618.tar.bz2
wallet-core-9c708251f92e6691ebba80fa8d129c6c04cec618.zip
wallet-core: emit DD37 self-transition notifications with errors
Diffstat (limited to 'packages/taler-wallet-core/src/util')
-rw-r--r--packages/taler-wallet-core/src/util/retries.ts306
1 files changed, 0 insertions, 306 deletions
diff --git a/packages/taler-wallet-core/src/util/retries.ts b/packages/taler-wallet-core/src/util/retries.ts
index e85eb0a6b..e602d4702 100644
--- a/packages/taler-wallet-core/src/util/retries.ts
+++ b/packages/taler-wallet-core/src/util/retries.ts
@@ -50,309 +50,3 @@ import { assertUnreachable } from "./assertUnreachable.js";
const logger = new Logger("util/retries.ts");
-export enum OperationAttemptResultType {
- Finished = "finished",
- Pending = "pending",
- Error = "error",
- Longpoll = "longpoll",
-}
-
-export type OperationAttemptResult<TSuccess = unknown, TPending = unknown> =
- | OperationAttemptFinishedResult<TSuccess>
- | OperationAttemptErrorResult
- | OperationAttemptLongpollResult
- | OperationAttemptPendingResult<TPending>;
-
-export namespace OperationAttemptResult {
- export function finishedEmpty(): OperationAttemptResult<unknown, unknown> {
- return {
- type: OperationAttemptResultType.Finished,
- result: undefined,
- };
- }
- export function pendingEmpty(): OperationAttemptResult<unknown, unknown> {
- return {
- type: OperationAttemptResultType.Pending,
- result: undefined,
- };
- }
- export function longpoll(): OperationAttemptResult<unknown, unknown> {
- return {
- type: OperationAttemptResultType.Longpoll,
- };
- }
-}
-
-export interface OperationAttemptFinishedResult<T> {
- type: OperationAttemptResultType.Finished;
- result: T;
-}
-
-export interface OperationAttemptPendingResult<T> {
- type: OperationAttemptResultType.Pending;
- result: T;
-}
-
-export interface OperationAttemptErrorResult {
- type: OperationAttemptResultType.Error;
- errorDetail: TalerErrorDetail;
-}
-
-export interface OperationAttemptLongpollResult {
- type: OperationAttemptResultType.Longpoll;
-}
-
-export interface RetryInfo {
- firstTry: AbsoluteTime;
- nextRetry: AbsoluteTime;
- retryCounter: number;
-}
-
-export interface RetryPolicy {
- readonly backoffDelta: Duration;
- readonly backoffBase: number;
- readonly maxTimeout: Duration;
-}
-
-const defaultRetryPolicy: RetryPolicy = {
- backoffBase: 1.5,
- backoffDelta: Duration.fromSpec({ seconds: 1 }),
- maxTimeout: Duration.fromSpec({ minutes: 2 }),
-};
-
-function updateTimeout(
- r: RetryInfo,
- p: RetryPolicy = defaultRetryPolicy,
-): void {
- const now = AbsoluteTime.now();
- if (now.t_ms === "never") {
- throw Error("assertion failed");
- }
- if (p.backoffDelta.d_ms === "forever") {
- r.nextRetry = AbsoluteTime.never();
- return;
- }
-
- const nextIncrement =
- p.backoffDelta.d_ms * Math.pow(p.backoffBase, r.retryCounter);
-
- const t =
- now.t_ms +
- (p.maxTimeout.d_ms === "forever"
- ? nextIncrement
- : Math.min(p.maxTimeout.d_ms, nextIncrement));
- r.nextRetry = AbsoluteTime.fromMilliseconds(t);
-}
-
-export namespace RetryInfo {
- export function getDuration(
- r: RetryInfo | undefined,
- p: RetryPolicy = defaultRetryPolicy,
- ): Duration {
- if (!r) {
- // If we don't have any retry info, run immediately.
- return { d_ms: 0 };
- }
- if (p.backoffDelta.d_ms === "forever") {
- return { d_ms: "forever" };
- }
- const t = p.backoffDelta.d_ms * Math.pow(p.backoffBase, r.retryCounter);
- return {
- d_ms:
- p.maxTimeout.d_ms === "forever" ? t : Math.min(p.maxTimeout.d_ms, t),
- };
- }
-
- export function reset(p: RetryPolicy = defaultRetryPolicy): RetryInfo {
- const now = AbsoluteTime.now();
- const info = {
- firstTry: now,
- nextRetry: now,
- retryCounter: 0,
- };
- updateTimeout(info, p);
- return info;
- }
-
- export function increment(
- r: RetryInfo | undefined,
- p: RetryPolicy = defaultRetryPolicy,
- ): RetryInfo {
- if (!r) {
- return reset(p);
- }
- const r2 = { ...r };
- r2.retryCounter++;
- updateTimeout(r2, p);
- return r2;
- }
-}
-
-/**
- * Parsed representation of task identifiers.
- */
-export type ParsedTaskIdentifier =
- | {
- tag: PendingTaskType.Withdraw;
- withdrawalGroupId: string;
- }
- | { tag: PendingTaskType.ExchangeUpdate; exchangeBaseUrl: string }
- | { tag: PendingTaskType.Backup; backupProviderBaseUrl: string }
- | { tag: PendingTaskType.Deposit; depositGroupId: string }
- | { tag: PendingTaskType.ExchangeCheckRefresh; exchangeBaseUrl: string }
- | { tag: PendingTaskType.ExchangeUpdate; exchangeBaseUrl: string }
- | { tag: PendingTaskType.PeerPullDebit; peerPullPaymentIncomingId: string }
- | { tag: PendingTaskType.PeerPullCredit; pursePub: string }
- | { tag: PendingTaskType.PeerPushCredit; peerPushPaymentIncomingId: string }
- | { tag: PendingTaskType.PeerPushDebit; pursePub: string }
- | { tag: PendingTaskType.Purchase; proposalId: string }
- | { tag: PendingTaskType.Recoup; recoupGroupId: string }
- | { tag: PendingTaskType.TipPickup; walletTipId: string }
- | { tag: PendingTaskType.Refresh; refreshGroupId: string };
-
-export function parseTaskIdentifier(x: string): ParsedTaskIdentifier {
- throw Error("not yet implemented");
-}
-
-export function constructTaskIdentifier(p: ParsedTaskIdentifier): TaskId {
- switch (p.tag) {
- case PendingTaskType.Backup:
- return `${p.tag}:${p.backupProviderBaseUrl}` as TaskId;
- case PendingTaskType.Deposit:
- return `${p.tag}:${p.depositGroupId}` as TaskId;
- case PendingTaskType.ExchangeCheckRefresh:
- return `${p.tag}:${p.exchangeBaseUrl}` as TaskId;
- case PendingTaskType.ExchangeUpdate:
- return `${p.tag}:${p.exchangeBaseUrl}` as TaskId;
- case PendingTaskType.PeerPullDebit:
- return `${p.tag}:${p.peerPullPaymentIncomingId}` as TaskId;
- case PendingTaskType.PeerPushCredit:
- return `${p.tag}:${p.peerPushPaymentIncomingId}` as TaskId;
- case PendingTaskType.PeerPullCredit:
- return `${p.tag}:${p.pursePub}` as TaskId;
- case PendingTaskType.PeerPushDebit:
- return `${p.tag}:${p.pursePub}` as TaskId;
- case PendingTaskType.Purchase:
- return `${p.tag}:${p.proposalId}` as TaskId;
- case PendingTaskType.Recoup:
- return `${p.tag}:${p.recoupGroupId}` as TaskId;
- case PendingTaskType.Refresh:
- return `${p.tag}:${p.refreshGroupId}` as TaskId;
- case PendingTaskType.TipPickup:
- return `${p.tag}:${p.walletTipId}` as TaskId;
- case PendingTaskType.Withdraw:
- return `${p.tag}:${p.withdrawalGroupId}` as TaskId;
- default:
- assertUnreachable(p);
- }
-}
-
-export namespace TaskIdentifiers {
- export function forWithdrawal(wg: WithdrawalGroupRecord): TaskId {
- return `${PendingTaskType.Withdraw}:${wg.withdrawalGroupId}` as TaskId;
- }
- export function forExchangeUpdate(exch: ExchangeRecord): TaskId {
- return `${PendingTaskType.ExchangeUpdate}:${exch.baseUrl}` as TaskId;
- }
- export function forExchangeUpdateFromUrl(exchBaseUrl: string): TaskId {
- return `${PendingTaskType.ExchangeUpdate}:${exchBaseUrl}` as TaskId;
- }
- export function forExchangeCheckRefresh(exch: ExchangeRecord): TaskId {
- return `${PendingTaskType.ExchangeCheckRefresh}:${exch.baseUrl}` as TaskId;
- }
- export function forTipPickup(tipRecord: TipRecord): TaskId {
- return `${PendingTaskType.TipPickup}:${tipRecord.walletTipId}` as TaskId;
- }
- export function forRefresh(refreshGroupRecord: RefreshGroupRecord): TaskId {
- return `${PendingTaskType.Refresh}:${refreshGroupRecord.refreshGroupId}` as TaskId;
- }
- export function forPay(purchaseRecord: PurchaseRecord): TaskId {
- return `${PendingTaskType.Purchase}:${purchaseRecord.proposalId}` as TaskId;
- }
- export function forRecoup(recoupRecord: RecoupGroupRecord): TaskId {
- return `${PendingTaskType.Recoup}:${recoupRecord.recoupGroupId}` as TaskId;
- }
- export function forDeposit(depositRecord: DepositGroupRecord): TaskId {
- return `${PendingTaskType.Deposit}:${depositRecord.depositGroupId}` as TaskId;
- }
- export function forBackup(backupRecord: BackupProviderRecord): TaskId {
- return `${PendingTaskType.Backup}:${backupRecord.baseUrl}` as TaskId;
- }
- export function forPeerPushPaymentInitiation(
- ppi: PeerPushPaymentInitiationRecord,
- ): TaskId {
- return `${PendingTaskType.PeerPushDebit}:${ppi.pursePub}` as TaskId;
- }
- export function forPeerPullPaymentInitiation(
- ppi: PeerPullPaymentInitiationRecord,
- ): TaskId {
- return `${PendingTaskType.PeerPullCredit}:${ppi.pursePub}` as TaskId;
- }
- export function forPeerPullPaymentDebit(
- ppi: PeerPullPaymentIncomingRecord,
- ): TaskId {
- return `${PendingTaskType.PeerPullDebit}:${ppi.peerPullPaymentIncomingId}` as TaskId;
- }
- export function forPeerPushCredit(
- ppi: PeerPushPaymentIncomingRecord,
- ): TaskId {
- return `${PendingTaskType.PeerPushCredit}:${ppi.peerPushPaymentIncomingId}` as TaskId;
- }
-}
-
-export async function scheduleRetryInTx(
- ws: InternalWalletState,
- tx: GetReadWriteAccess<{
- operationRetries: typeof WalletStoresV1.operationRetries;
- }>,
- opId: string,
- errorDetail?: TalerErrorDetail,
-): Promise<void> {
- let retryRecord = await tx.operationRetries.get(opId);
- if (!retryRecord) {
- retryRecord = {
- id: opId,
- retryInfo: RetryInfo.reset(),
- };
- if (errorDetail) {
- retryRecord.lastError = errorDetail;
- }
- } else {
- retryRecord.retryInfo = RetryInfo.increment(retryRecord.retryInfo);
- if (errorDetail) {
- retryRecord.lastError = errorDetail;
- } else {
- delete retryRecord.lastError;
- }
- }
- await tx.operationRetries.put(retryRecord);
-}
-
-export async function scheduleRetry(
- ws: InternalWalletState,
- opId: string,
- errorDetail?: TalerErrorDetail,
-): Promise<void> {
- return await ws.db
- .mktx((x) => [x.operationRetries])
- .runReadWrite(async (tx) => {
- tx.operationRetries;
- scheduleRetryInTx(ws, tx, opId, errorDetail);
- });
-}
-
-/**
- * Run an operation handler, expect a success result and extract the success value.
- */
-export async function unwrapOperationHandlerResultOrThrow<T>(
- res: OperationAttemptResult<T>,
-): Promise<T> {
- switch (res.type) {
- case OperationAttemptResultType.Finished:
- return res.result;
- case OperationAttemptResultType.Error:
- throw TalerError.fromUncheckedDetail(res.errorDetail);
- default:
- throw Error(`unexpected operation result (${res.type})`);
- }
-}