From 525a66bcca5ee58814573d3810c0fbc02e937883 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Tue, 27 Feb 2024 15:46:45 +0100 Subject: wallet-core: types for observability --- packages/taler-util/src/index.ts | 1 + packages/taler-util/src/notifications.ts | 73 +++++++++++++---- packages/taler-wallet-core/src/wallet.ts | 17 ++++ packages/taler-wallet-core/src/withdraw.ts | 5 -- .../src/cta/Withdraw/state.ts | 91 ++++++++++++++-------- 5 files changed, 131 insertions(+), 56 deletions(-) (limited to 'packages') diff --git a/packages/taler-util/src/index.ts b/packages/taler-util/src/index.ts index d9ab60910..28909facb 100644 --- a/packages/taler-util/src/index.ts +++ b/packages/taler-util/src/index.ts @@ -43,6 +43,7 @@ export { setPRNG, } from "./nacl-fast.js"; export * from "./notifications.js"; +export * from "./observability.js"; export * from "./operation.js"; export * from "./payto.js"; export * from "./promises.js"; diff --git a/packages/taler-util/src/notifications.ts b/packages/taler-util/src/notifications.ts index d84d3706d..5e335f7ad 100644 --- a/packages/taler-util/src/notifications.ts +++ b/packages/taler-util/src/notifications.ts @@ -22,17 +22,18 @@ /** * Imports. */ -import { WithdrawalOperationStatus } from "./index.node.js"; import { TransactionState } from "./transactions-types.js"; import { ExchangeEntryState, TalerErrorDetail } from "./wallet-types.js"; export enum NotificationType { BalanceChange = "balance-change", BackupOperationError = "backup-error", - PendingOperationProcessed = "pending-operation-processed", TransactionStateTransition = "transaction-state-transition", ExchangeStateTransition = "exchange-state-transition", - WithdrawalOperationTransition = "withdrawal-state-transition", + TaskProgress = "task-progress", + RequestProgress = "request-progress", + RequestOnTaskDependency = "request-on-task-dependency", + TaskOnTaskDependency = "task-on-task-dependency", } export interface ErrorInfoSummary { @@ -97,26 +98,64 @@ export interface BalanceChangeNotification { hintTransactionId: string; } -export interface BackupOperationErrorNotification { - type: NotificationType.BackupOperationError; - error: TalerErrorDetail; +export interface TaskProgressNotification { + type: NotificationType.TaskProgress; + taskId: string; +} + +export interface RequestProgressNotification { + type: NotificationType.RequestProgress; + requestId: string; +} + +export interface RequestOnTaskDependencyNotification { + type: NotificationType.RequestOnTaskDependency; + parentTaskId: string; + childTaskId: string; } -export interface PendingOperationProcessedNotification { - type: NotificationType.PendingOperationProcessed; - id: string; - taskResultType: string; +export interface TaskOnTaskDependencyNotification { + type: NotificationType.TaskOnTaskDependency; + parentRequestId: string; + childTaskId: string; } -export interface WithdrawalOperationTransitionNotification { - type: NotificationType.WithdrawalOperationTransition; - operationId: string; - state: WithdrawalOperationStatus; + +export type ObservabilityEvent = + | { + type: "start-http-fetch"; + url: string; + } + | { + type: "finish-http-fetch"; + url: string; + status: number; + } + | { + type: "start-db-query"; + name: string; + location: string; + } + | { + type: "finish-db-query"; + name: string; + location: string; + } + | { + type: "task-processed"; + taskResultType: string; + }; + +export interface BackupOperationErrorNotification { + type: NotificationType.BackupOperationError; + error: TalerErrorDetail; } export type WalletNotification = | BalanceChangeNotification | BackupOperationErrorNotification | ExchangeStateTransitionNotification - | PendingOperationProcessedNotification - | WithdrawalOperationTransitionNotification - | TransactionStateTransitionNotification; + | TransactionStateTransitionNotification + | TaskProgressNotification + | RequestProgressNotification + | RequestOnTaskDependencyNotification + | TaskOnTaskDependencyNotification; diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index bab054f4b..779eefe26 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -27,6 +27,7 @@ import { AmountString, Amounts, AsyncCondition, + CancellationToken, CoinDumpJson, CoinStatus, CoreApiResponse, @@ -41,6 +42,7 @@ import { ListGlobalCurrencyAuditorsResponse, ListGlobalCurrencyExchangesResponse, Logger, + ObservabilityContext, OpenedPromise, PrepareWithdrawExchangeRequest, PrepareWithdrawExchangeResponse, @@ -269,6 +271,21 @@ import { const logger = new Logger("wallet.ts"); +/** + * Execution context for code that is run in the wallet. + * + * Typically the excecution context is either for a wallet-core + * request handler or for a shepherded task. + */ +export interface WalletExecutionContext { + readonly ws: InternalWalletState; + readonly cryptoApi: TalerCryptoInterface; + readonly cancellationToken: CancellationToken; + readonly http: HttpRequestLibrary; + readonly db: DbAccess; + readonly oc: ObservabilityContext; +} + export const EXCHANGE_COINS_LOCK = "exchange-coins-lock"; export const EXCHANGE_RESERVES_LOCK = "exchange-reserves-lock"; diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts index 25c1e5129..541525c6b 100644 --- a/packages/taler-wallet-core/src/withdraw.ts +++ b/packages/taler-wallet-core/src/withdraw.ts @@ -1975,11 +1975,6 @@ export async function getWithdrawalDetailsForUri( 2, )}`, ); - ws.notify({ - type: NotificationType.WithdrawalOperationTransition, - operationId: info.operationId, - state: resp.type === "fail" ? info.status : resp.body.status, - }); ongoingChecks[talerWithdrawUri] = false; }); } diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts index 515c5765e..a49ad3d36 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts +++ b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts @@ -22,7 +22,7 @@ import { ExchangeListItem, NotificationType, TalerError, - parseWithdrawExchangeUri + parseWithdrawExchangeUri, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; @@ -140,8 +140,8 @@ export function useComponentStateFromParams({ confirm: { onClick: isValid ? pushAlertOnError(async () => { - onAmountChanged(Amounts.stringify(amount)); - }) + onAmountChanged(Amounts.stringify(amount)); + }) : undefined, }, amount: { @@ -208,10 +208,17 @@ export function useComponentStateFromURI({ WalletApiOperation.GetWithdrawalDetailsForUri, { talerWithdrawUri, - notifyChangeFromPendingTimeoutMs: 30 * 1000 + notifyChangeFromPendingTimeoutMs: 30 * 1000, }, ); - const { amount, defaultExchangeBaseUrl, possibleExchanges, operationId, confirmTransferUrl, status } = uriInfo; + const { + amount, + defaultExchangeBaseUrl, + possibleExchanges, + operationId, + confirmTransferUrl, + status, + } = uriInfo; const transaction = await api.wallet.call( WalletApiOperation.GetWithdrawalTransactionByUri, { talerWithdrawUri }, @@ -228,16 +235,16 @@ export function useComponentStateFromURI({ }; }); - const readyToListen = uriInfoHook && !uriInfoHook.hasError + const readyToListen = uriInfoHook && !uriInfoHook.hasError; useEffect(() => { if (!uriInfoHook) { return; } return api.listener.onUpdateNotification( - [NotificationType.WithdrawalOperationTransition], + [NotificationType.TransactionStateTransition], () => { - uriInfoHook.retry() + uriInfoHook.retry(); }, ); }, [readyToListen]); @@ -282,14 +289,14 @@ export function useComponentStateFromURI({ if (uriInfoHook.response.status !== "pending") { if (uriInfoHook.response.transaction) { - onSuccess(uriInfoHook.response.transaction.transactionId) + onSuccess(uriInfoHook.response.transaction.transactionId); } return { status: "already-completed", operationState: uriInfoHook.response.status, confirmTransferUrl: uriInfoHook.response.confirmTransferUrl, error: undefined, - } + }; } return useCallback(() => { @@ -302,7 +309,7 @@ export function useComponentStateFromURI({ exchangeList, defaultExchange, ); - }, []) + }, []); } type ManualOrManagedWithdrawFunction = ( @@ -330,13 +337,18 @@ function exchangeSelectionState( return selectedExchange; } - return useCallback((): State.Success | State.LoadingUriError | State.Loading => { + return useCallback((): + | State.Success + | State.LoadingUriError + | State.Loading => { const { i18n } = useTranslationContext(); const { pushAlertOnError } = useAlertContext(); const [ageRestricted, setAgeRestricted] = useState(0); const currentExchange = selectedExchange.selected; - const [selectedCurrency, setSelectedCurrency] = useState(chosenAmount.currency) + const [selectedCurrency, setSelectedCurrency] = useState( + chosenAmount.currency, + ); /** * With the exchange and amount, ask the wallet the information * about the withdrawal @@ -359,7 +371,7 @@ function exchangeSelectionState( return { amount: withdrawAmount, ageRestrictionOptions: info.ageRestrictionOptions, - accounts: info.withdrawalAccountsList + accounts: info.withdrawalAccountsList, }; }, []); @@ -424,23 +436,33 @@ function exchangeSelectionState( //TODO: calculate based on exchange info const ageRestriction = ageRestrictionEnabled ? { - list: ageRestrictionOptions, - value: String(ageRestricted), - onChange: pushAlertOnError(async (v: string) => - setAgeRestricted(parseInt(v, 10)), - ), - } + list: ageRestrictionOptions, + value: String(ageRestricted), + onChange: pushAlertOnError(async (v: string) => + setAgeRestricted(parseInt(v, 10)), + ), + } : undefined; - const altCurrencies = amountHook.response.accounts.filter(a => !!a.currencySpecification).map(a => a.currencySpecification!.name) - const chooseCurrencies = altCurrencies.length === 0 ? [] : [toBeReceived.currency, ...altCurrencies] - const convAccount = amountHook.response.accounts.find(c => { - return c.currencySpecification && c.currencySpecification.name === selectedCurrency - }) - const conversionInfo = !convAccount ? undefined : ({ - spec: convAccount.currencySpecification!, - amount: Amounts.parseOrThrow(convAccount.transferAmount!) - }) + const altCurrencies = amountHook.response.accounts + .filter((a) => !!a.currencySpecification) + .map((a) => a.currencySpecification!.name); + const chooseCurrencies = + altCurrencies.length === 0 + ? [] + : [toBeReceived.currency, ...altCurrencies]; + const convAccount = amountHook.response.accounts.find((c) => { + return ( + c.currencySpecification && + c.currencySpecification.name === selectedCurrency + ); + }); + const conversionInfo = !convAccount + ? undefined + : { + spec: convAccount.currencySpecification!, + amount: Amounts.parseOrThrow(convAccount.transferAmount!), + }; return { status: "success", @@ -450,17 +472,18 @@ function exchangeSelectionState( toBeReceived, chooseCurrencies, selectedCurrency, - changeCurrency: (s) => { setSelectedCurrency(s) }, + changeCurrency: (s) => { + setSelectedCurrency(s); + }, conversionInfo, withdrawalFee, chosenAmount, talerWithdrawUri, ageRestriction, doWithdrawal: { - onClick: - doingWithdraw - ? undefined - : pushAlertOnError(doWithdrawAndCheckError), + onClick: doingWithdraw + ? undefined + : pushAlertOnError(doWithdrawAndCheckError), }, cancel, }; -- cgit v1.2.3