diff options
author | Florian Dold <florian@dold.me> | 2024-01-30 15:05:01 +0100 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2024-01-30 15:05:01 +0100 |
commit | 0e79a79e6b9f159b3aebc39f2e278f062c4d4410 (patch) | |
tree | 30273b53255d22a567f1f6eec4f6c11cabbd72d7 | |
parent | 2d2c43a3015a52de9a4c08a329e51b3e713380dc (diff) | |
download | wallet-core-0e79a79e6b9f159b3aebc39f2e278f062c4d4410.tar.xz |
formatting, comments, always return httpResp in OperationResult
m--------- | build-system/taler-build-scripts | 0 | ||||
-rw-r--r-- | packages/aml-backoffice-ui/src/pages/AntiMoneyLaunderingForm.tsx | 113 | ||||
-rw-r--r-- | packages/demobank-ui/src/hooks/circuit.ts | 22 | ||||
-rw-r--r-- | packages/taler-harness/src/integrationtests/testrunner.ts | 2 | ||||
-rw-r--r-- | packages/taler-util/src/MerchantApiClient.ts | 26 | ||||
-rw-r--r-- | packages/taler-util/src/http-client/authentication.ts | 58 | ||||
-rw-r--r-- | packages/taler-util/src/http-client/bank-conversion.ts | 161 | ||||
-rw-r--r-- | packages/taler-util/src/http-client/bank-core.ts | 778 | ||||
-rw-r--r-- | packages/taler-util/src/http-client/bank-integration.ts | 131 | ||||
-rw-r--r-- | packages/taler-util/src/http-client/bank-revenue.ts | 72 | ||||
-rw-r--r-- | packages/taler-util/src/http-client/bank-wire.ts | 173 | ||||
-rw-r--r-- | packages/taler-util/src/http-client/exchange.ts | 161 | ||||
-rw-r--r-- | packages/taler-util/src/http-client/merchant.ts | 48 | ||||
-rw-r--r-- | packages/taler-util/src/http-client/officer-account.ts | 26 | ||||
-rw-r--r-- | packages/taler-util/src/operation.ts | 155 |
15 files changed, 1338 insertions, 588 deletions
diff --git a/build-system/taler-build-scripts b/build-system/taler-build-scripts -Subproject 001f5dd081fc8729ff8def90c4a1c3f93eb8689 +Subproject 23538677f6c6be2a62f38dc6137ecdd1c76b7b1 diff --git a/packages/aml-backoffice-ui/src/pages/AntiMoneyLaunderingForm.tsx b/packages/aml-backoffice-ui/src/pages/AntiMoneyLaunderingForm.tsx index aa1247d5f..21faff058 100644 --- a/packages/aml-backoffice-ui/src/pages/AntiMoneyLaunderingForm.tsx +++ b/packages/aml-backoffice-ui/src/pages/AntiMoneyLaunderingForm.tsx @@ -1,19 +1,44 @@ -import { AbsoluteTime, AmountJson, Amounts, Codec, OperationResult, buildCodecForObject, codecForNumber, codecForString, codecOptional } from "@gnu-taler/taler-util"; -import { DefaultForm, useTranslationContext } from "@gnu-taler/web-util/browser"; +import { + AbsoluteTime, + AmountJson, + Amounts, + Codec, + OperationResult, + buildCodecForObject, + codecForNumber, + codecForString, + codecOptional, +} from "@gnu-taler/taler-util"; +import { + DefaultForm, + useTranslationContext, +} from "@gnu-taler/web-util/browser"; import { h } from "preact"; import { useExchangeApiContext } from "../context/config.js"; import { FormMetadata, uiForms } from "../forms/declaration.js"; import { Pages } from "../pages.js"; import { AmlExchangeBackend } from "../utils/types.js"; -export function AntiMoneyLaunderingForm({ account, formId, onSubmit }: { account: string, formId: string, onSubmit: (justification: Justification, state: AmlExchangeBackend.AmlState, threshold: AmountJson) => Promise<void>; }) { - const { i18n } = useTranslationContext() - const theForm = uiForms.forms(i18n).find((v) => v.id === formId) +export function AntiMoneyLaunderingForm({ + account, + formId, + onSubmit, +}: { + account: string; + formId: string; + onSubmit: ( + justification: Justification, + state: AmlExchangeBackend.AmlState, + threshold: AmountJson, + ) => Promise<void>; +}) { + const { i18n } = useTranslationContext(); + const theForm = uiForms.forms(i18n).find((v) => v.id === formId); if (!theForm) { - return <div>form with id {formId} not found</div> + return <div>form with id {formId} not found</div>; } - const { config } = useExchangeApiContext() + const { config } = useExchangeApiContext(); const initial = { when: AbsoluteTime.now(), @@ -24,9 +49,10 @@ export function AntiMoneyLaunderingForm({ account, formId, onSubmit }: { account <DefaultForm initial={initial} form={theForm.impl(initial)} - onUpdate={() => { }} + onUpdate={() => {}} onSubmit={(formValue) => { - if (formValue.state === undefined || formValue.threshold === undefined) return; + if (formValue.state === undefined || formValue.threshold === undefined) + return; const st = formValue.state; const amount = formValue.threshold; @@ -34,8 +60,8 @@ export function AntiMoneyLaunderingForm({ account, formId, onSubmit }: { account id: theForm.id, label: theForm.label, version: theForm.version, - value: formValue - } + value: formValue, + }; onSubmit(justification, st, amount); }} @@ -61,17 +87,16 @@ export function AntiMoneyLaunderingForm({ account, formId, onSubmit }: { account export type Justification<T = any> = { // form values value: T; -} & Omit<Omit<FormMetadata<any>, "icon">, "impl"> +} & Omit<Omit<FormMetadata<any>, "icon">, "impl">; export function stringifyJustification(j: Justification): string { - return JSON.stringify(j) + return JSON.stringify(j); } - type SimpleFormMetadata = { - version?: number, - id?: string, -} + version?: number; + id?: string; +}; export const codecForSimpleFormMetadata = (): Codec<SimpleFormMetadata> => buildCodecForObject<SimpleFormMetadata>() @@ -80,52 +105,62 @@ export const codecForSimpleFormMetadata = (): Codec<SimpleFormMetadata> => .build("SimpleFormMetadata"); type ParseJustificationFail = - "not-json" | - "id-not-found" | - "form-not-found" | - "version-not-found"; + | "not-json" + | "id-not-found" + | "form-not-found" + | "version-not-found"; -export function parseJustification(s: string, listOfAllKnownForms: FormMetadata<any>[]): OperationResult<{ justification: Justification, metadata: FormMetadata<any> }, ParseJustificationFail> { +export function parseJustification( + s: string, + listOfAllKnownForms: FormMetadata<any>[], +): OperationResult< + { justification: Justification; metadata: FormMetadata<any> }, + ParseJustificationFail +> { try { - const justification = JSON.parse(s) - const info = codecForSimpleFormMetadata().decode(justification) + const justification = JSON.parse(s); + const info = codecForSimpleFormMetadata().decode(justification); if (!info.id) { return { + httpResp: undefined as any, type: "fail", case: "id-not-found", - detail: {} as any - } + detail: {} as any, + }; } if (!info.version) { return { + httpResp: undefined as any, type: "fail", case: "version-not-found", - detail: {} as any - } + detail: {} as any, + }; } const found = listOfAllKnownForms.find((f) => { - return f.id === info.id && f.version === info.version - }) + return f.id === info.id && f.version === info.version; + }); if (!found) { return { + httpResp: undefined as any, type: "fail", case: "form-not-found", - detail: {} as any - } + detail: {} as any, + }; } return { + httpResp: undefined as any, type: "ok", body: { - justification, metadata: found - } - } + justification, + metadata: found, + }, + }; } catch (e) { return { + httpResp: undefined as any, type: "fail", case: "not-json", - detail: {} as any - } + detail: {} as any, + }; } - } - diff --git a/packages/demobank-ui/src/hooks/circuit.ts b/packages/demobank-ui/src/hooks/circuit.ts index 0c306259b..2b0781465 100644 --- a/packages/demobank-ui/src/hooks/circuit.ts +++ b/packages/demobank-ui/src/hooks/circuit.ts @@ -218,7 +218,7 @@ export function useOnePendingCashouts(account: string) { const pendingCashout = list.body.cashouts.find( (c) => c.status === "pending", ); - if (!pendingCashout) return opFixedSuccess(undefined); + if (!pendingCashout) return opFixedSuccess(list.httpResp, undefined); const cashoutInfo = await api.getCashoutById( { username, token }, pendingCashout?.cashout_id, @@ -226,7 +226,7 @@ export function useOnePendingCashouts(account: string) { if (cashoutInfo.type !== "ok") { return cashoutInfo; } - return opFixedSuccess({ + return opFixedSuccess(list.httpResp, { ...cashoutInfo.body, id: pendingCashout.cashout_id, }); @@ -275,21 +275,17 @@ export function useCashouts(account: string) { return list; } const all: Array<CashoutWithId | undefined> = await Promise.all( - list.body.cashouts.map((c) => { - return api - .getCashoutById({ username, token }, c.cashout_id) - .then((r) => { - if (r.type === "fail") { - return undefined; - } - return { ...r.body, id: c.cashout_id }; - }); + list.body.cashouts.map(async (c) => { + const r = await api.getCashoutById({ username, token }, c.cashout_id); + if (r.type === "fail") { + return undefined; + } + return { ...r.body, id: c.cashout_id }; }), ); const cashouts = all.filter(notUndefined); - return { type: "ok" as const, body: { cashouts } }; + return { type: "ok" as const, body: { cashouts }, httpResp: list.httpResp }; } - const { data, error } = useSWR< | OperationOk<{ cashouts: CashoutWithId[] }> | TalerCoreBankErrorsByMethod<"getAccountCashouts">, diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts index 35b179d87..81047e267 100644 --- a/packages/taler-harness/src/integrationtests/testrunner.ts +++ b/packages/taler-harness/src/integrationtests/testrunner.ts @@ -101,6 +101,7 @@ import { runWithdrawalNotifyBeforeTxTest } from "./test-withdrawal-notify-before import { runWalletDd48Test } from "./test-wallet-dd48.js"; import { runMultiExchangeTest } from "./test-multiexchange.js"; import { runWalletRefreshTest } from "./test-wallet-refresh.js"; +import { runOtpTest } from "./test-otp.js"; /** * Test runner. @@ -191,6 +192,7 @@ const allTests: TestMainFunction[] = [ runWalletDd48Test, runCurrencyScopeTest, runWalletRefreshTest, + runOtpTest, ]; export interface TestRunSpec { diff --git a/packages/taler-util/src/MerchantApiClient.ts b/packages/taler-util/src/MerchantApiClient.ts index fe523cd43..86c72651e 100644 --- a/packages/taler-util/src/MerchantApiClient.ts +++ b/packages/taler-util/src/MerchantApiClient.ts @@ -69,6 +69,23 @@ export interface CreateMerchantTippingReserveRequest { wire_method: string; } +interface OtpDeviceAddDetails { + // Device ID to use. + otp_device_id: string; + + // Human-readable description for the device. + otp_device_description: string; + + // A base64-encoded key + otp_key: string; + + // Algorithm for computing the POS confirmation. + otp_algorithm: number; + + // Counter for counter-based OTP devices. + otp_ctr?: number; +} + export interface DeleteTippingReserveArgs { reservePub: string; purge?: boolean; @@ -321,6 +338,15 @@ export class MerchantApiClient { } } + async createOtpDevice(req: OtpDeviceAddDetails): Promise<void> { + let url = new URL("private/templates", this.baseUrl); + const resp = await this.httpClient.fetch(url.href, { + method: "POST", + body: req, + headers: this.makeAuthHeader(), + }); + } + private makeAuthHeader(): Record<string, string> { switch (this.auth.method) { case "external": diff --git a/packages/taler-util/src/http-client/authentication.ts b/packages/taler-util/src/http-client/authentication.ts index 66e00ded5..d53894a33 100644 --- a/packages/taler-util/src/http-client/authentication.ts +++ b/packages/taler-util/src/http-client/authentication.ts @@ -14,11 +14,27 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ + /** + * Imports. + */ import { HttpStatusCode } from "../http-status-codes.js"; -import { HttpRequestLibrary, createPlatformHttpLib, makeBasicAuthHeader } from "../http.js"; +import { + HttpRequestLibrary, + createPlatformHttpLib, + makeBasicAuthHeader, +} from "../http.js"; import { LibtoolVersion } from "../libtool-version.js"; -import { opEmptySuccess, opKnownHttpFailure, opSuccess, opUnknownFailure } from "../operation.js"; -import { AccessToken, TalerAuthentication, codecForTokenSuccessResponse } from "./types.js"; +import { + opEmptySuccess, + opKnownHttpFailure, + opSuccess, + opUnknownFailure, +} from "../operation.js"; +import { + AccessToken, + TalerAuthentication, + codecForTokenSuccessResponse, +} from "./types.js"; import { makeBearerTokenAuthHeader } from "./utils.js"; export class TalerAuthenticationHttpClient { @@ -35,14 +51,14 @@ export class TalerAuthenticationHttpClient { } isCompatible(version: string): boolean { - const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version) - return compare?.compatible ?? false + const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version); + return compare?.compatible ?? false; } /** * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-token - * - * @returns + * + * @returns */ async createAccessToken( password: string, @@ -54,14 +70,18 @@ export class TalerAuthenticationHttpClient { headers: { Authorization: makeBasicAuthHeader(this.username, password), }, - body + body, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForTokenSuccessResponse()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForTokenSuccessResponse()); //FIXME: missing in docs - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp) - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp) - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } @@ -71,14 +91,16 @@ export class TalerAuthenticationHttpClient { method: "DELETE", headers: { Authorization: makeBearerTokenAuthHeader(token), - } + }, }); switch (resp.status) { - case HttpStatusCode.Ok: return opEmptySuccess() + case HttpStatusCode.Ok: + return opEmptySuccess(resp); //FIXME: missing in docs - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp) - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } - -}
\ No newline at end of file +} diff --git a/packages/taler-util/src/http-client/bank-conversion.ts b/packages/taler-util/src/http-client/bank-conversion.ts index 3ecb036ee..5242a5fc6 100644 --- a/packages/taler-util/src/http-client/bank-conversion.ts +++ b/packages/taler-util/src/http-client/bank-conversion.ts @@ -1,9 +1,35 @@ +/* + This file is part of GNU Taler + (C) 2022-2024 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** + * Imports. + */ import { AmountJson, Amounts } from "../amounts.js"; import { HttpRequestLibrary } from "../http-common.js"; import { HttpStatusCode } from "../http-status-codes.js"; import { createPlatformHttpLib } from "../http.js"; import { LibtoolVersion } from "../libtool-version.js"; -import { FailCasesByMethod, ResultByMethod, opEmptySuccess, opKnownHttpFailure, opSuccess, opUnknownFailure } from "../operation.js"; +import { + FailCasesByMethod, + ResultByMethod, + opEmptySuccess, + opKnownHttpFailure, + opSuccess, + opUnknownFailure, +} from "../operation.js"; import { TalerErrorCode } from "../taler-error-codes.js"; import { codecForTalerErrorDetail } from "../wallet-types.js"; import { @@ -11,12 +37,16 @@ import { TalerBankConversionApi, codecForCashinConversionResponse, codecForCashoutConversionResponse, - codecForConversionBankConfig + codecForConversionBankConfig, } from "./types.js"; import { makeBearerTokenAuthHeader } from "./utils.js"; -export type TalerBankConversionResultByMethod<prop extends keyof TalerBankConversionHttpClient> = ResultByMethod<TalerBankConversionHttpClient, prop> -export type TalerBankConversionErrorsByMethod<prop extends keyof TalerBankConversionHttpClient> = FailCasesByMethod<TalerBankConversionHttpClient, prop> +export type TalerBankConversionResultByMethod< + prop extends keyof TalerBankConversionHttpClient, +> = ResultByMethod<TalerBankConversionHttpClient, prop>; +export type TalerBankConversionErrorsByMethod< + prop extends keyof TalerBankConversionHttpClient, +> = FailCasesByMethod<TalerBankConversionHttpClient, prop>; /** * The API is used by the wallets. @@ -34,112 +64,145 @@ export class TalerBankConversionHttpClient { } isCompatible(version: string): boolean { - const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version) - return compare?.compatible ?? false + const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version); + return compare?.compatible ?? false; } /** * https://docs.taler.net/core/api-bank-conversion-info.html#get--config - * + * */ async getConfig() { const url = new URL(`config`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { - method: "GET" + method: "GET", }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForConversionBankConfig()) - case HttpStatusCode.NotImplemented: return opKnownHttpFailure(resp.status, resp) - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForConversionBankConfig()); + case HttpStatusCode.NotImplemented: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-bank-conversion-info.html#get--cashin-rate - * + * */ - async getCashinRate(conversion: { debit?: AmountJson, credit?: AmountJson }) { + async getCashinRate(conversion: { debit?: AmountJson; credit?: AmountJson }) { const url = new URL(`cashin-rate`, this.baseUrl); if (conversion.debit) { - url.searchParams.set("amount_debit", Amounts.stringify(conversion.debit)) + url.searchParams.set("amount_debit", Amounts.stringify(conversion.debit)); } if (conversion.credit) { - url.searchParams.set("amount_credit", Amounts.stringify(conversion.credit)) + url.searchParams.set( + "amount_credit", + Amounts.stringify(conversion.credit), + ); } const resp = await this.httpLib.fetch(url.href, { method: "GET", }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForCashinConversionResponse()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForCashinConversionResponse()); case HttpStatusCode.BadRequest: { - const body = await resp.json() - const details = codecForTalerErrorDetail().decode(body) + const body = await resp.json(); + const details = codecForTalerErrorDetail().decode(body); switch (details.code) { - case TalerErrorCode.GENERIC_PARAMETER_MISSING: return opKnownHttpFailure(resp.status, resp); - case TalerErrorCode.GENERIC_PARAMETER_MALFORMED: return opKnownHttpFailure(resp.status, resp); - case TalerErrorCode.GENERIC_CURRENCY_MISMATCH: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, body) + case TalerErrorCode.GENERIC_PARAMETER_MISSING: + return opKnownHttpFailure(resp.status, resp); + case TalerErrorCode.GENERIC_PARAMETER_MALFORMED: + return opKnownHttpFailure(resp.status, resp); + case TalerErrorCode.GENERIC_CURRENCY_MISMATCH: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, body); } } - case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotImplemented: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Conflict: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotImplemented: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-bank-conversion-info.html#get--cashout-rate - * + * */ - async getCashoutRate(conversion: { debit?: AmountJson, credit?: AmountJson }) { + async getCashoutRate(conversion: { + debit?: AmountJson; + credit?: AmountJson; + }) { const url = new URL(`cashout-rate`, this.baseUrl); if (conversion.debit) { - url.searchParams.set("amount_debit", Amounts.stringify(conversion.debit)) + url.searchParams.set("amount_debit", Amounts.stringify(conversion.debit)); } if (conversion.credit) { - url.searchParams.set("amount_credit", Amounts.stringify(conversion.credit)) + url.searchParams.set( + "amount_credit", + Amounts.stringify(conversion.credit), + ); } const resp = await this.httpLib.fetch(url.href, { method: "GET", }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForCashoutConversionResponse()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForCashoutConversionResponse()); case HttpStatusCode.BadRequest: { - const body = await resp.json() - const details = codecForTalerErrorDetail().decode(body) + const body = await resp.json(); + const details = codecForTalerErrorDetail().decode(body); switch (details.code) { - case TalerErrorCode.GENERIC_PARAMETER_MISSING: return opKnownHttpFailure(resp.status, resp); - case TalerErrorCode.GENERIC_PARAMETER_MALFORMED: return opKnownHttpFailure(resp.status, resp); - case TalerErrorCode.GENERIC_CURRENCY_MISMATCH: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, body) + case TalerErrorCode.GENERIC_PARAMETER_MISSING: + return opKnownHttpFailure(resp.status, resp); + case TalerErrorCode.GENERIC_PARAMETER_MALFORMED: + return opKnownHttpFailure(resp.status, resp); + case TalerErrorCode.GENERIC_CURRENCY_MISMATCH: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, body); } } - case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotImplemented: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Conflict: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotImplemented: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-bank-conversion-info.html#post--conversion-rate - * + * */ - async updateConversionRate(auth: AccessToken, body: TalerBankConversionApi.ConversionRate) { + async updateConversionRate( + auth: AccessToken, + body: TalerBankConversionApi.ConversionRate, + ) { const url = new URL(`conversion-rate`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "POST", headers: { - Authorization: makeBearerTokenAuthHeader(auth) + Authorization: makeBearerTokenAuthHeader(auth), }, - body + body, }); switch (resp.status) { - case HttpStatusCode.NoContent: return opEmptySuccess() - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotImplemented: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.NoContent: + return opEmptySuccess(resp); + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotImplemented: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } - } - diff --git a/packages/taler-util/src/http-client/bank-core.ts b/packages/taler-util/src/http-client/bank-core.ts index 8a3283d0f..90ba88b6a 100644 --- a/packages/taler-util/src/http-client/bank-core.ts +++ b/packages/taler-util/src/http-client/bank-core.ts @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - (C) 2022 Taler Systems S.A. + (C) 2022-2024 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -24,24 +24,59 @@ import { codecForTanTransmission, opKnownAlternativeFailure, opKnownHttpFailure, - opKnownTalerFailure + opKnownTalerFailure, } from "@gnu-taler/taler-util"; import { HttpRequestLibrary, - createPlatformHttpLib + createPlatformHttpLib, } from "@gnu-taler/taler-util/http"; -import { FailCasesByMethod, ResultByMethod, opEmptySuccess, opFixedSuccess, opSuccess, opUnknownFailure } from "../operation.js"; +import { + FailCasesByMethod, + ResultByMethod, + opEmptySuccess, + opFixedSuccess, + opSuccess, + opUnknownFailure, +} from "../operation.js"; import { TalerAuthenticationHttpClient } from "./authentication.js"; import { TalerBankConversionHttpClient } from "./bank-conversion.js"; import { TalerBankIntegrationHttpClient } from "./bank-integration.js"; import { TalerRevenueHttpClient } from "./bank-revenue.js"; import { TalerWireGatewayHttpClient } from "./bank-wire.js"; -import { AccessToken, PaginationParams, TalerCorebankApi, UserAndToken, WithdrawalOperationStatus, codecForAccountData, codecForBankAccountCreateWithdrawalResponse, codecForBankAccountTransactionInfo, codecForBankAccountTransactionsResponse, codecForCashoutPending, codecForCashoutStatusResponse, codecForCashouts, codecForCoreBankConfig, codecForCreateTransactionResponse, codecForGlobalCashouts, codecForListBankAccountsResponse, codecForMonitorResponse, codecForPublicAccountsResponse, codecForRegisterAccountResponse, codecForWithdrawalPublicInfo } from "./types.js"; -import { addLongPollingParam, addPaginationParams, makeBearerTokenAuthHeader } from "./utils.js"; - - -export type TalerCoreBankResultByMethod<prop extends keyof TalerCoreBankHttpClient> = ResultByMethod<TalerCoreBankHttpClient, prop> -export type TalerCoreBankErrorsByMethod<prop extends keyof TalerCoreBankHttpClient> = FailCasesByMethod<TalerCoreBankHttpClient, prop> +import { + AccessToken, + PaginationParams, + TalerCorebankApi, + UserAndToken, + WithdrawalOperationStatus, + codecForAccountData, + codecForBankAccountCreateWithdrawalResponse, + codecForBankAccountTransactionInfo, + codecForBankAccountTransactionsResponse, + codecForCashoutPending, + codecForCashoutStatusResponse, + codecForCashouts, + codecForCoreBankConfig, + codecForCreateTransactionResponse, + codecForGlobalCashouts, + codecForListBankAccountsResponse, + codecForMonitorResponse, + codecForPublicAccountsResponse, + codecForRegisterAccountResponse, + codecForWithdrawalPublicInfo, +} from "./types.js"; +import { + addLongPollingParam, + addPaginationParams, + makeBearerTokenAuthHeader, +} from "./utils.js"; + +export type TalerCoreBankResultByMethod< + prop extends keyof TalerCoreBankHttpClient, +> = ResultByMethod<TalerCoreBankHttpClient, prop>; +export type TalerCoreBankErrorsByMethod< + prop extends keyof TalerCoreBankHttpClient, +> = FailCasesByMethod<TalerCoreBankHttpClient, prop>; /** * Protocol version spoken with the core bank. @@ -49,7 +84,7 @@ export type TalerCoreBankErrorsByMethod<prop extends keyof TalerCoreBankHttpClie * Endpoint must be ordered in the same way that in the docs * Response code (http and taler) must have the same order that in the docs * That way is easier to see changes - * + * * Uses libtool's current:revision:age versioning. */ export class TalerCoreBankHttpClient { @@ -65,22 +100,24 @@ export class TalerCoreBankHttpClient { } isCompatible(version: string): boolean { - const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version) - return compare?.compatible ?? false + const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version); + return compare?.compatible ?? false; } /** * https://docs.taler.net/core/api-corebank.html#config - * + * */ async getConfig() { const url = new URL(`config`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { - method: "GET" + method: "GET", }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForCoreBankConfig()) - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForCoreBankConfig()); + default: + return opUnknownFailure(resp, await resp.text()); } } @@ -90,42 +127,58 @@ export class TalerCoreBankHttpClient { /** * https://docs.taler.net/core/api-corebank.html#post--accounts - * + * */ - async createAccount(auth: AccessToken, body: TalerCorebankApi.RegisterAccountRequest) { + async createAccount( + auth: AccessToken, + body: TalerCorebankApi.RegisterAccountRequest, + ) { const url = new URL(`accounts`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "POST", body, headers: { - Authorization: makeBearerTokenAuthHeader(auth) + Authorization: makeBearerTokenAuthHeader(auth), }, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForRegisterAccountResponse()) - case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Ok: + return opSuccess(resp, codecForRegisterAccountResponse()); + case HttpStatusCode.BadRequest: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: { - const body = await resp.json() - const details = codecForTalerErrorDetail().decode(body) + const body = await resp.json(); + const details = codecForTalerErrorDetail().decode(body); switch (details.code) { - case TalerErrorCode.BANK_REGISTER_USERNAME_REUSE: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_REGISTER_PAYTO_URI_REUSE: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_UNALLOWED_DEBIT: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_NON_ADMIN_SET_TAN_CHANNEL: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_TAN_CHANNEL_NOT_SUPPORTED: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_MISSING_TAN_INFO: return opKnownTalerFailure(details.code, resp); - default: return opUnknownFailure(resp, body) + case TalerErrorCode.BANK_REGISTER_USERNAME_REUSE: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_REGISTER_PAYTO_URI_REUSE: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_UNALLOWED_DEBIT: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_NON_ADMIN_SET_TAN_CHANNEL: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_TAN_CHANNEL_NOT_SUPPORTED: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_MISSING_TAN_INFO: + return opKnownTalerFailure(details.code, resp); + default: + return opUnknownFailure(resp, body); } } - default: return opUnknownFailure(resp, await resp.text()) + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-corebank.html#delete--accounts-$USERNAME - * + * */ async deleteAccount(auth: UserAndToken, cid?: string) { const url = new URL(`accounts/${auth.username}`, this.baseUrl); @@ -137,28 +190,44 @@ export class TalerCoreBankHttpClient { }, }); switch (resp.status) { - case HttpStatusCode.Accepted: return opKnownAlternativeFailure(resp, resp.status, codecForChallenge()) - case HttpStatusCode.NoContent: return opEmptySuccess() - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Accepted: + return opKnownAlternativeFailure( + resp, + resp.status, + codecForChallenge(), + ); + case HttpStatusCode.NoContent: + return opEmptySuccess(resp); + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: { - const body = await resp.json() - const details = codecForTalerErrorDetail().decode(body) + const body = await resp.json(); + const details = codecForTalerErrorDetail().decode(body); switch (details.code) { - case TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_ACCOUNT_BALANCE_NOT_ZERO: return opKnownTalerFailure(details.code, resp); - default: return opUnknownFailure(resp, body) + case TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_ACCOUNT_BALANCE_NOT_ZERO: + return opKnownTalerFailure(details.code, resp); + default: + return opUnknownFailure(resp, body); } } - default: return opUnknownFailure(resp, await resp.text()) + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-corebank.html#patch--accounts-$USERNAME - * + * */ - async updateAccount(auth: UserAndToken, body: TalerCorebankApi.AccountReconfiguration, cid?: string) { + async updateAccount( + auth: UserAndToken, + body: TalerCorebankApi.AccountReconfiguration, + cid?: string, + ) { const url = new URL(`accounts/${auth.username}`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "PATCH", @@ -169,31 +238,50 @@ export class TalerCoreBankHttpClient { }, }); switch (resp.status) { - case HttpStatusCode.Accepted: return opKnownAlternativeFailure(resp, resp.status, codecForChallenge()) - case HttpStatusCode.NoContent: return opEmptySuccess() - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Accepted: + return opKnownAlternativeFailure( + resp, + resp.status, + codecForChallenge(), + ); + case HttpStatusCode.NoContent: + return opEmptySuccess(resp); + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: { - const body = await resp.json() - const details = codecForTalerErrorDetail().decode(body) + const body = await resp.json(); + const details = codecForTalerErrorDetail().decode(body); switch (details.code) { - case TalerErrorCode.BANK_NON_ADMIN_PATCH_LEGAL_NAME: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_NON_ADMIN_PATCH_CASHOUT: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_TAN_CHANNEL_NOT_SUPPORTED: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_MISSING_TAN_INFO: return opKnownTalerFailure(details.code, resp); - default: return opUnknownFailure(resp, body) + case TalerErrorCode.BANK_NON_ADMIN_PATCH_LEGAL_NAME: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_NON_ADMIN_PATCH_CASHOUT: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_TAN_CHANNEL_NOT_SUPPORTED: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_MISSING_TAN_INFO: + return opKnownTalerFailure(details.code, resp); + default: + return opUnknownFailure(resp, body); } } - default: return opUnknownFailure(resp, await resp.text()) + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-corebank.html#patch--accounts-$USERNAME-auth - * + * */ - async updatePassword(auth: UserAndToken, body: TalerCorebankApi.AccountPasswordChange, cid?: string) { + async updatePassword( + auth: UserAndToken, + body: TalerCorebankApi.AccountPasswordChange, + cid?: string, + ) { const url = new URL(`accounts/${auth.username}/auth`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "PATCH", @@ -204,85 +292,116 @@ export class TalerCoreBankHttpClient { }, }); switch (resp.status) { - case HttpStatusCode.Accepted: return opKnownAlternativeFailure(resp, resp.status, codecForChallenge()) - case HttpStatusCode.NoContent: return opEmptySuccess() - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Accepted: + return opKnownAlternativeFailure( + resp, + resp.status, + codecForChallenge(), + ); + case HttpStatusCode.NoContent: + return opEmptySuccess(resp); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: { - const body = await resp.json() - const details = codecForTalerErrorDetail().decode(body) + const body = await resp.json(); + const details = codecForTalerErrorDetail().decode(body); switch (details.code) { - case TalerErrorCode.BANK_NON_ADMIN_PATCH_MISSING_OLD_PASSWORD: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_PATCH_BAD_OLD_PASSWORD: return opKnownTalerFailure(details.code, resp); - default: return opUnknownFailure(resp, body) + case TalerErrorCode.BANK_NON_ADMIN_PATCH_MISSING_OLD_PASSWORD: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_PATCH_BAD_OLD_PASSWORD: + return opKnownTalerFailure(details.code, resp); + default: + return opUnknownFailure(resp, body); } } - default: return opUnknownFailure(resp, await resp.text()) + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-corebank.html#get--public-accounts - * + * */ - async getPublicAccounts(filter: { account?: string } = {}, pagination?: PaginationParams) { + async getPublicAccounts( + filter: { account?: string } = {}, + pagination?: PaginationParams, + ) { const url = new URL(`public-accounts`, this.baseUrl); - addPaginationParams(url, pagination) + addPaginationParams(url, pagination); if (filter.account !== undefined) { - url.searchParams.set("filter_name", filter.account) + url.searchParams.set("filter_name", filter.account); } const resp = await this.httpLib.fetch(url.href, { method: "GET", }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForPublicAccountsResponse()) - case HttpStatusCode.NoContent: return opFixedSuccess({ public_accounts: [] }) - case HttpStatusCode.NotFound: return opFixedSuccess({ public_accounts: [] }) - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForPublicAccountsResponse()); + case HttpStatusCode.NoContent: + return opFixedSuccess(resp, { public_accounts: [] }); + case HttpStatusCode.NotFound: + return opFixedSuccess(resp, { public_accounts: [] }); + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-corebank.html#get--accounts - * + * */ - async getAccounts(auth: AccessToken, filter: { account?: string } = {}, pagination?: PaginationParams) { + async getAccounts( + auth: AccessToken, + filter: { account?: string } = {}, + pagination?: PaginationParams, + ) { const url = new URL(`accounts`, this.baseUrl); - addPaginationParams(url, pagination) + addPaginationParams(url, pagination); if (filter.account !== undefined) { - url.searchParams.set("filter_name", filter.account) + url.searchParams.set("filter_name", filter.account); } const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { - Authorization: makeBearerTokenAuthHeader(auth) + Authorization: makeBearerTokenAuthHeader(auth), }, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForListBankAccountsResponse()) - case HttpStatusCode.NoContent: return opFixedSuccess({ accounts: [] }) - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForListBankAccountsResponse()); + case HttpStatusCode.NoContent: + return opFixedSuccess(resp, { accounts: [] }); + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-corebank.html#get--accounts-$USERNAME - * + * */ async getAccount(auth: UserAndToken) { const url = new URL(`accounts/${auth.username}`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { - Authorization: makeBearerTokenAuthHeader(auth.token) + Authorization: makeBearerTokenAuthHeader(auth.token), }, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForAccountData()) - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForAccountData()); + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } @@ -292,52 +411,71 @@ export class TalerCoreBankHttpClient { /** * https://docs.taler.net/core/api-corebank.html#get--accounts-$USERNAME-transactions - * + * */ - async getTransactions(auth: UserAndToken, params?: PaginationParams & LongPollParams) { + async getTransactions( + auth: UserAndToken, + params?: PaginationParams & LongPollParams, + ) { const url = new URL(`accounts/${auth.username}/transactions`, this.baseUrl); - addPaginationParams(url, params) - addLongPollingParam(url, params) + addPaginationParams(url, params); + addLongPollingParam(url, params); const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { - Authorization: makeBearerTokenAuthHeader(auth.token) + Authorization: makeBearerTokenAuthHeader(auth.token), }, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForBankAccountTransactionsResponse()) - case HttpStatusCode.NoContent: return opFixedSuccess({ transactions: [] }) - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForBankAccountTransactionsResponse()); + case HttpStatusCode.NoContent: + return opFixedSuccess(resp, { transactions: [] }); + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-corebank.html#get--accounts-$USERNAME-transactions-$TRANSACTION_ID - * + * */ async getTransactionById(auth: UserAndToken, txid: number) { - const url = new URL(`accounts/${auth.username}/transactions/${String(txid)}`, this.baseUrl); + const url = new URL( + `accounts/${auth.username}/transactions/${String(txid)}`, + this.baseUrl, + ); const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { - Authorization: makeBearerTokenAuthHeader(auth.token) + Authorization: makeBearerTokenAuthHeader(auth.token), }, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForBankAccountTransactionInfo()) - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForBankAccountTransactionInfo()); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-transactions - * + * */ - async createTransaction(auth: UserAndToken, body: TalerCorebankApi.CreateTransactionRequest, cid?: string) { + async createTransaction( + auth: UserAndToken, + body: TalerCorebankApi.CreateTransactionRequest, + cid?: string, + ) { const url = new URL(`accounts/${auth.username}/transactions`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "POST", @@ -348,22 +486,36 @@ export class TalerCoreBankHttpClient { body, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForCreateTransactionResponse()) - case HttpStatusCode.Accepted: return opKnownAlternativeFailure(resp, resp.status, codecForChallenge()) - case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Ok: + return opSuccess(resp, codecForCreateTransactionResponse()); + case HttpStatusCode.Accepted: + return opKnownAlternativeFailure( + resp, + resp.status, + codecForChallenge(), + ); + case HttpStatusCode.BadRequest: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: { - const body = await resp.json() - const details = codecForTalerErrorDetail().decode(body) + const body = await resp.json(); + const details = codecForTalerErrorDetail().decode(body); switch (details.code) { - case TalerErrorCode.BANK_SAME_ACCOUNT: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_UNKNOWN_CREDITOR: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_UNALLOWED_DEBIT: return opKnownTalerFailure(details.code, resp); - default: return opUnknownFailure(resp, body) + case TalerErrorCode.BANK_SAME_ACCOUNT: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_UNKNOWN_CREDITOR: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_UNALLOWED_DEBIT: + return opKnownTalerFailure(details.code, resp); + default: + return opUnknownFailure(resp, body); } } - default: return opUnknownFailure(resp, await resp.text()) + default: + return opUnknownFailure(resp, await resp.text()); } } @@ -373,33 +525,44 @@ export class TalerCoreBankHttpClient { /** * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-withdrawals - * + * */ - async createWithdrawal(auth: UserAndToken, body: TalerCorebankApi.BankAccountCreateWithdrawalRequest) { + async createWithdrawal( + auth: UserAndToken, + body: TalerCorebankApi.BankAccountCreateWithdrawalRequest, + ) { const url = new URL(`accounts/${auth.username}/withdrawals`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "POST", headers: { - Authorization: makeBearerTokenAuthHeader(auth.token) + Authorization: makeBearerTokenAuthHeader(auth.token), }, body, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForBankAccountCreateWithdrawalResponse()) - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Ok: + return opSuccess(resp, codecForBankAccountCreateWithdrawalResponse()); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Conflict: + return opKnownHttpFailure(resp.status, resp); //FIXME: missing in docs - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-withdrawals-$WITHDRAWAL_ID-confirm - * + * */ async confirmWithdrawalById(auth: UserAndToken, wid: string, cid?: string) { - const url = new URL(`accounts/${auth.username}/withdrawals/${wid}/confirm`, this.baseUrl); + const url = new URL( + `accounts/${auth.username}/withdrawals/${wid}/confirm`, + this.baseUrl, + ); const resp = await this.httpLib.fetch(url.href, { method: "POST", headers: { @@ -408,68 +571,99 @@ export class TalerCoreBankHttpClient { }, }); switch (resp.status) { - case HttpStatusCode.Accepted: return opKnownAlternativeFailure(resp, resp.status, codecForChallenge()) - case HttpStatusCode.NoContent: return opEmptySuccess() + case HttpStatusCode.Accepted: + return opKnownAlternativeFailure( + resp, + resp.status, + codecForChallenge(), + ); + case HttpStatusCode.NoContent: + return opEmptySuccess(resp); //FIXME: missing in docs - case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp) - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp) + case HttpStatusCode.BadRequest: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: { - const body = await resp.json() - const details = codecForTalerErrorDetail().decode(body) + const body = await resp.json(); + const details = codecForTalerErrorDetail().decode(body); switch (details.code) { - case TalerErrorCode.BANK_CONFIRM_ABORT_CONFLICT: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_CONFIRM_INCOMPLETE: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_UNALLOWED_DEBIT: return opKnownTalerFailure(details.code, resp); - default: return opUnknownFailure(resp, body) + case TalerErrorCode.BANK_CONFIRM_ABORT_CONFLICT: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_CONFIRM_INCOMPLETE: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_UNALLOWED_DEBIT: + return opKnownTalerFailure(details.code, resp); + default: + return opUnknownFailure(resp, body); } } - default: return opUnknownFailure(resp, await resp.text()) + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-withdrawals-$WITHDRAWAL_ID-abort - * + * */ async abortWithdrawalById(auth: UserAndToken, wid: string) { - const url = new URL(`accounts/${auth.username}/withdrawals/${wid}/abort`, this.baseUrl); + const url = new URL( + `accounts/${auth.username}/withdrawals/${wid}/abort`, + this.baseUrl, + ); const resp = await this.httpLib.fetch(url.href, { method: "POST", headers: { - Authorization: makeBearerTokenAuthHeader(auth.token) + Authorization: makeBearerTokenAuthHeader(auth.token), }, }); switch (resp.status) { - case HttpStatusCode.NoContent: return opEmptySuccess() + case HttpStatusCode.NoContent: + return opEmptySuccess(resp); //FIXME: missing in docs - case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp) - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp) - case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.BadRequest: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Conflict: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-corebank.html#get--withdrawals-$WITHDRAWAL_ID - * + * */ - async getWithdrawalById(wid: string, params?: { - old_state?: WithdrawalOperationStatus, - } & LongPollParams) { + async getWithdrawalById( + wid: string, + params?: { + old_state?: WithdrawalOperationStatus; + } & LongPollParams, + ) { const url = new URL(`withdrawals/${wid}`, this.baseUrl); - addLongPollingParam(url, params) + addLongPollingParam(url, params); if (params) { - url.searchParams.set("old_state", !params.old_state ? "pending" : params.old_state) + url.searchParams.set( + "old_state", + !params.old_state ? "pending" : params.old_state, + ); } const resp = await this.httpLib.fetch(url.href, { method: "GET", }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForWithdrawalPublicInfo()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForWithdrawalPublicInfo()); //FIXME: missing in docs - case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp) - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp) - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.BadRequest: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } @@ -479,9 +673,13 @@ export class TalerCoreBankHttpClient { /** * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-cashouts - * + * */ - async createCashout(auth: UserAndToken, body: TalerCorebankApi.CashoutRequest, cid?: string) { + async createCashout( + auth: UserAndToken, + body: TalerCorebankApi.CashoutRequest, + cid?: string, + ) { const url = new URL(`accounts/${auth.username}/cashouts`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "POST", @@ -492,92 +690,123 @@ export class TalerCoreBankHttpClient { body, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForCashoutPending()) - case HttpStatusCode.Accepted: return opKnownAlternativeFailure(resp, resp.status, codecForChallenge()) - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForCashoutPending()); + case HttpStatusCode.Accepted: + return opKnownAlternativeFailure( + resp, + resp.status, + codecForChallenge(), + ); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: { - const body = await resp.json() - const details = codecForTalerErrorDetail().decode(body) + const body = await resp.json(); + const details = codecForTalerErrorDetail().decode(body); switch (details.code) { - case TalerErrorCode.BANK_TRANSFER_REQUEST_UID_REUSED: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_BAD_CONVERSION: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_UNALLOWED_DEBIT: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_CONFIRM_INCOMPLETE: return opKnownTalerFailure(details.code, resp); - default: return opUnknownFailure(resp, body) + case TalerErrorCode.BANK_TRANSFER_REQUEST_UID_REUSED: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_BAD_CONVERSION: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_UNALLOWED_DEBIT: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_CONFIRM_INCOMPLETE: + return opKnownTalerFailure(details.code, resp); + default: + return opUnknownFailure(resp, body); } } case HttpStatusCode.BadGateway: { - const body = await resp.json() - const details = codecForTalerErrorDetail().decode(body) + const body = await resp.json(); + const details = codecForTalerErrorDetail().decode(body); switch (details.code) { - case TalerErrorCode.BANK_TAN_CHANNEL_SCRIPT_FAILED: return opKnownTalerFailure(details.code, resp); - default: return opUnknownFailure(resp, body) + case TalerErrorCode.BANK_TAN_CHANNEL_SCRIPT_FAILED: + return opKnownTalerFailure(details.code, resp); + default: + return opUnknownFailure(resp, body); } } - case HttpStatusCode.NotImplemented: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.NotImplemented: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-corebank.html#get--accounts-$USERNAME-cashouts-$CASHOUT_ID - * + * */ async getCashoutById(auth: UserAndToken, cid: number) { - const url = new URL(`accounts/${auth.username}/cashouts/${cid}`, this.baseUrl); + const url = new URL( + `accounts/${auth.username}/cashouts/${cid}`, + this.baseUrl, + ); const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { - Authorization: makeBearerTokenAuthHeader(auth.token) + Authorization: makeBearerTokenAuthHeader(auth.token), }, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForCashoutStatusResponse()) - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotImplemented: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForCashoutStatusResponse()); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotImplemented: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-corebank.html#get--accounts-$USERNAME-cashouts - * + * */ async getAccountCashouts(auth: UserAndToken, pagination?: PaginationParams) { const url = new URL(`accounts/${auth.username}/cashouts`, this.baseUrl); - addPaginationParams(url, pagination) + addPaginationParams(url, pagination); const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { - Authorization: makeBearerTokenAuthHeader(auth.token) + Authorization: makeBearerTokenAuthHeader(auth.token), }, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForCashouts()) - case HttpStatusCode.NoContent: return opFixedSuccess({ cashouts: [] }); - case HttpStatusCode.NotImplemented: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForCashouts()); + case HttpStatusCode.NoContent: + return opFixedSuccess(resp, { cashouts: [] }); + case HttpStatusCode.NotImplemented: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-corebank.html#get--cashouts - * + * */ async getGlobalCashouts(auth: AccessToken, pagination?: PaginationParams) { const url = new URL(`cashouts`, this.baseUrl); - addPaginationParams(url, pagination) + addPaginationParams(url, pagination); const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { - Authorization: makeBearerTokenAuthHeader(auth) + Authorization: makeBearerTokenAuthHeader(auth), }, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForGlobalCashouts()) - case HttpStatusCode.NoContent: return opFixedSuccess({ cashouts: [] }); - case HttpStatusCode.NotImplemented: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForGlobalCashouts()); + case HttpStatusCode.NoContent: + return opFixedSuccess(resp, { cashouts: [] }); + case HttpStatusCode.NotImplemented: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } @@ -587,60 +816,84 @@ export class TalerCoreBankHttpClient { /** * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-challenge-$CHALLENGE_ID - * + * */ async sendChallenge(auth: UserAndToken, cid: string) { - const url = new URL(`accounts/${auth.username}/challenge/${cid}`, this.baseUrl); + const url = new URL( + `accounts/${auth.username}/challenge/${cid}`, + this.baseUrl, + ); const resp = await this.httpLib.fetch(url.href, { method: "POST", headers: { - Authorization: makeBearerTokenAuthHeader(auth.token) + Authorization: makeBearerTokenAuthHeader(auth.token), }, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForTanTransmission()) - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Ok: + return opSuccess(resp, codecForTanTransmission()); + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.BadGateway: { - const body = await resp.json() - const details = codecForTalerErrorDetail().decode(body) + const body = await resp.json(); + const details = codecForTalerErrorDetail().decode(body); switch (details.code) { - case TalerErrorCode.BANK_TAN_CHANNEL_SCRIPT_FAILED: return opKnownTalerFailure(details.code, resp); - default: return opUnknownFailure(resp, body) + case TalerErrorCode.BANK_TAN_CHANNEL_SCRIPT_FAILED: + return opKnownTalerFailure(details.code, resp); + default: + return opUnknownFailure(resp, body); } } - default: return opUnknownFailure(resp, await resp.text()) + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-challenge-$CHALLENGE_ID-confirm - * + * */ - async confirmChallenge(auth: UserAndToken, cid: string, body: TalerCorebankApi.ChallengeSolve) { - const url = new URL(`accounts/${auth.username}/challenge/${cid}/confirm`, this.baseUrl); + async confirmChallenge( + auth: UserAndToken, + cid: string, + body: TalerCorebankApi.ChallengeSolve, + ) { + const url = new URL( + `accounts/${auth.username}/challenge/${cid}/confirm`, + this.baseUrl, + ); const resp = await this.httpLib.fetch(url.href, { method: "POST", headers: { - Authorization: makeBearerTokenAuthHeader(auth.token) + Authorization: makeBearerTokenAuthHeader(auth.token), }, body, }); switch (resp.status) { - case HttpStatusCode.NoContent: return opEmptySuccess() - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NoContent: + return opEmptySuccess(resp); + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: { - const body = await resp.json() - const details = codecForTalerErrorDetail().decode(body) + const body = await resp.json(); + const details = codecForTalerErrorDetail().decode(body); switch (details.code) { - case TalerErrorCode.BANK_TAN_CHALLENGE_EXPIRED: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_TAN_CHALLENGE_FAILED: return opKnownTalerFailure(details.code, resp); - default: return opUnknownFailure(resp, body) + case TalerErrorCode.BANK_TAN_CHALLENGE_EXPIRED: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_TAN_CHALLENGE_FAILED: + return opKnownTalerFailure(details.code, resp); + default: + return opUnknownFailure(resp, body); } } - case HttpStatusCode.TooManyRequests: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.TooManyRequests: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } @@ -650,77 +903,92 @@ export class TalerCoreBankHttpClient { /** * https://docs.taler.net/core/api-corebank.html#get--monitor - * + * */ - async getMonitor(auth: AccessToken, params: { timeframe?: TalerCorebankApi.MonitorTimeframeParam, which?: number } = {}) { + async getMonitor( + auth: AccessToken, + params: { + timeframe?: TalerCorebankApi.MonitorTimeframeParam; + which?: number; + } = {}, + ) { const url = new URL(`monitor`, this.baseUrl); if (params.timeframe) { - url.searchParams.set("timeframe", TalerCorebankApi.MonitorTimeframeParam[params.timeframe]) + url.searchParams.set( + "timeframe", + TalerCorebankApi.MonitorTimeframeParam[params.timeframe], + ); } if (params.which) { - url.searchParams.set("which", String(params.which)) + url.searchParams.set("which", String(params.which)); } const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { - Authorization: makeBearerTokenAuthHeader(auth) + Authorization: makeBearerTokenAuthHeader(auth), }, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForMonitorResponse()) - case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForMonitorResponse()); + case HttpStatusCode.BadRequest: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } - // // Others API // /** * https://docs.taler.net/core/api-corebank.html#taler-bank-integration-api - * + * */ getIntegrationAPI(): TalerBankIntegrationHttpClient { const url = new URL(`taler-integration/`, this.baseUrl); - return new TalerBankIntegrationHttpClient(url.href, this.httpLib) + return new TalerBankIntegrationHttpClient(url.href, this.httpLib); } /** * https://docs.taler.net/core/api-corebank.html#taler-bank-integration-api - * + * */ getWireGatewayAPI(username: string): TalerWireGatewayHttpClient { - const url = new URL(`accounts/${username}/taler-wire-gateway/`, this.baseUrl); - return new TalerWireGatewayHttpClient(url.href, username, this.httpLib) + const url = new URL( + `accounts/${username}/taler-wire-gateway/`, + this.baseUrl, + ); + return new TalerWireGatewayHttpClient(url.href, username, this.httpLib); } /** - * https://docs.taler.net/core/api-corebank.html#taler-bank-integration-api - * - */ + * https://docs.taler.net/core/api-corebank.html#taler-bank-integration-api + * + */ getRevenueAPI(username: string): TalerRevenueHttpClient { const url = new URL(`accounts/${username}/taler-revenue/`, this.baseUrl); - return new TalerRevenueHttpClient(url.href, username, this.httpLib,) + return new TalerRevenueHttpClient(url.href, username, this.httpLib); } /** * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-token - * - */ + * + */ getAuthenticationAPI(username: string): TalerAuthenticationHttpClient { const url = new URL(`accounts/${username}/`, this.baseUrl); - return new TalerAuthenticationHttpClient(url.href, username, this.httpLib,) + return new TalerAuthenticationHttpClient(url.href, username, this.httpLib); } /** * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-token - * - */ + * + */ getConversionInfoAPI(): TalerBankConversionHttpClient { const url = new URL(`conversion-info/`, this.baseUrl); - return new TalerBankConversionHttpClient(url.href, this.httpLib) + return new TalerBankConversionHttpClient(url.href, this.httpLib); } } diff --git a/packages/taler-util/src/http-client/bank-integration.ts b/packages/taler-util/src/http-client/bank-integration.ts index 2264b65bf..938249daf 100644 --- a/packages/taler-util/src/http-client/bank-integration.ts +++ b/packages/taler-util/src/http-client/bank-integration.ts @@ -1,8 +1,32 @@ -import { HttpRequestLibrary, readSuccessResponseJsonOrThrow } from "../http-common.js"; +/* + This file is part of GNU Taler + (C) 2022-2024 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +import { HttpRequestLibrary } from "../http-common.js"; import { HttpStatusCode } from "../http-status-codes.js"; import { createPlatformHttpLib } from "../http.js"; import { LibtoolVersion } from "../libtool-version.js"; -import { FailCasesByMethod, ResultByMethod, opEmptySuccess, opKnownHttpFailure, opKnownTalerFailure, opSuccess, opUnknownFailure } from "../operation.js"; +import { + FailCasesByMethod, + ResultByMethod, + opEmptySuccess, + opKnownHttpFailure, + opKnownTalerFailure, + opSuccess, + opUnknownFailure, +} from "../operation.js"; import { TalerErrorCode } from "../taler-error-codes.js"; import { codecForTalerErrorDetail } from "../wallet-types.js"; import { @@ -11,12 +35,16 @@ import { WithdrawalOperationStatus, codecForBankWithdrawalOperationPostResponse, codecForBankWithdrawalOperationStatus, - codecForIntegrationBankConfig + codecForIntegrationBankConfig, } from "./types.js"; import { addLongPollingParam } from "./utils.js"; -export type TalerBankIntegrationResultByMethod<prop extends keyof TalerBankIntegrationHttpClient> = ResultByMethod<TalerBankIntegrationHttpClient, prop> -export type TalerBankIntegrationErrorsByMethod<prop extends keyof TalerBankIntegrationHttpClient> = FailCasesByMethod<TalerBankIntegrationHttpClient, prop> +export type TalerBankIntegrationResultByMethod< + prop extends keyof TalerBankIntegrationHttpClient, +> = ResultByMethod<TalerBankIntegrationHttpClient, prop>; +export type TalerBankIntegrationErrorsByMethod< + prop extends keyof TalerBankIntegrationHttpClient, +> = FailCasesByMethod<TalerBankIntegrationHttpClient, prop>; /** * The API is used by the wallets. @@ -34,78 +62,100 @@ export class TalerBankIntegrationHttpClient { } isCompatible(version: string): boolean { - const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version) - return compare?.compatible ?? false + const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version); + return compare?.compatible ?? false; } /** * https://docs.taler.net/core/api-bank-integration.html#get--config - * + * */ async getConfig() { const url = new URL(`config`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { - method: "GET" + method: "GET", }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForIntegrationBankConfig()) - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForIntegrationBankConfig()); + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-bank-integration.html#get--withdrawal-operation-$WITHDRAWAL_ID - * + * */ - async getWithdrawalOperationById(woid: string, params?: { - old_state?: WithdrawalOperationStatus - } & LongPollParams) { + async getWithdrawalOperationById( + woid: string, + params?: { + old_state?: WithdrawalOperationStatus; + } & LongPollParams, + ) { const url = new URL(`withdrawal-operation/${woid}`, this.baseUrl); - addLongPollingParam(url, params) + addLongPollingParam(url, params); if (params) { - url.searchParams.set("old_state", !params.old_state ? "pending" : params.old_state) + url.searchParams.set( + "old_state", + !params.old_state ? "pending" : params.old_state, + ); } const resp = await this.httpLib.fetch(url.href, { - method: "GET" + method: "GET", }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForBankWithdrawalOperationStatus()) - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp) - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForBankWithdrawalOperationStatus()); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-bank-integration.html#post-$BANK_API_BASE_URL-withdrawal-operation-$wopid - * + * */ - async completeWithdrawalOperationById(woid: string, body: TalerBankIntegrationApi.BankWithdrawalOperationPostRequest) { + async completeWithdrawalOperationById( + woid: string, + body: TalerBankIntegrationApi.BankWithdrawalOperationPostRequest, + ) { const url = new URL(`withdrawal-operation/${woid}`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "POST", body, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForBankWithdrawalOperationPostResponse()) - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForBankWithdrawalOperationPostResponse()); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: { - const body = await resp.json() - const details = codecForTalerErrorDetail().decode(body) + const body = await resp.json(); + const details = codecForTalerErrorDetail().decode(body); switch (details.code) { - case TalerErrorCode.BANK_WITHDRAWAL_OPERATION_RESERVE_SELECTION_CONFLICT: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_DUPLICATE_RESERVE_PUB_SUBJECT: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_UNKNOWN_ACCOUNT: return opKnownTalerFailure(details.code, resp); - case TalerErrorCode.BANK_ACCOUNT_IS_NOT_EXCHANGE: return opKnownTalerFailure(details.code, resp); - default: return opUnknownFailure(resp, body) + case TalerErrorCode.BANK_WITHDRAWAL_OPERATION_RESERVE_SELECTION_CONFLICT: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_DUPLICATE_RESERVE_PUB_SUBJECT: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_UNKNOWN_ACCOUNT: + return opKnownTalerFailure(details.code, resp); + case TalerErrorCode.BANK_ACCOUNT_IS_NOT_EXCHANGE: + return opKnownTalerFailure(details.code, resp); + default: + return opUnknownFailure(resp, body); } } - default: return opUnknownFailure(resp, await resp.text()) + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-bank-integration.html#post-$BANK_API_BASE_URL-withdrawal-operation-$wopid - * + * */ async abortWithdrawalOperationById(woid: string) { const url = new URL(`withdrawal-operation/${woid}/abort`, this.baseUrl); @@ -113,11 +163,14 @@ export class TalerBankIntegrationHttpClient { method: "POST", }); switch (resp.status) { - case HttpStatusCode.NoContent: return opEmptySuccess() - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp) - case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.NoContent: + return opEmptySuccess(resp); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Conflict: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } } - diff --git a/packages/taler-util/src/http-client/bank-revenue.ts b/packages/taler-util/src/http-client/bank-revenue.ts index 9dfcc4f64..fb90f9c1c 100644 --- a/packages/taler-util/src/http-client/bank-revenue.ts +++ b/packages/taler-util/src/http-client/bank-revenue.ts @@ -1,15 +1,45 @@ -import { HttpRequestLibrary, makeBasicAuthHeader, readSuccessResponseJsonOrThrow } from "../http-common.js"; +/* + This file is part of GNU Taler + (C) 2022-2024 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +import { HttpRequestLibrary, makeBasicAuthHeader } from "../http-common.js"; import { HttpStatusCode } from "../http-status-codes.js"; import { createPlatformHttpLib } from "../http.js"; -import { FailCasesByMethod, ResultByMethod, opKnownHttpFailure, opSuccess, opUnknownFailure } from "../operation.js"; -import { LongPollParams, PaginationParams, TalerRevenueApi, codecForMerchantIncomingHistory } from "./types.js"; +import { + FailCasesByMethod, + ResultByMethod, + opKnownHttpFailure, + opSuccess, + opUnknownFailure, +} from "../operation.js"; +import { + LongPollParams, + PaginationParams, + codecForMerchantIncomingHistory, +} from "./types.js"; import { addLongPollingParam, addPaginationParams } from "./utils.js"; -export type TalerBankRevenueResultByMethod<prop extends keyof TalerRevenueHttpClient> = ResultByMethod<TalerRevenueHttpClient, prop> -export type TalerBankRevenueErrorsByMethod<prop extends keyof TalerRevenueHttpClient> = FailCasesByMethod<TalerRevenueHttpClient, prop> +export type TalerBankRevenueResultByMethod< + prop extends keyof TalerRevenueHttpClient, +> = ResultByMethod<TalerRevenueHttpClient, prop>; +export type TalerBankRevenueErrorsByMethod< + prop extends keyof TalerRevenueHttpClient, +> = FailCasesByMethod<TalerRevenueHttpClient, prop>; /** - * The API is used by the merchant (or other parties) to query + * The API is used by the merchant (or other parties) to query * for incoming transactions to their account. */ export class TalerRevenueHttpClient { @@ -30,7 +60,7 @@ export class TalerRevenueHttpClient { // /** // * https://docs.taler.net/core/api-corebank.html#config - // * + // * // */ // async getConfig() { // const url = new URL(`config`, this.baseUrl); @@ -43,28 +73,32 @@ export class TalerRevenueHttpClient { // } // } - /** * https://docs.taler.net/core/api-bank-revenue.html#get--history - * - * @returns + * + * @returns */ async getHistory(auth: string, params?: PaginationParams & LongPollParams) { const url = new URL(`history`, this.baseUrl); - addPaginationParams(url, params) - addLongPollingParam(url, params) + addPaginationParams(url, params); + addLongPollingParam(url, params); const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { Authorization: makeBasicAuthHeader(this.username, auth), - } + }, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForMerchantIncomingHistory()) - case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForMerchantIncomingHistory()); + case HttpStatusCode.BadRequest: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } -}
\ No newline at end of file +} diff --git a/packages/taler-util/src/http-client/bank-wire.ts b/packages/taler-util/src/http-client/bank-wire.ts index 90c3aeae5..ba68d26ef 100644 --- a/packages/taler-util/src/http-client/bank-wire.ts +++ b/packages/taler-util/src/http-client/bank-wire.ts @@ -1,18 +1,53 @@ +/* + This file is part of GNU Taler + (C) 2022-2024 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + import { HttpRequestLibrary, makeBasicAuthHeader } from "../http-common.js"; import { HttpStatusCode } from "../http-status-codes.js"; import { createPlatformHttpLib } from "../http.js"; -import { FailCasesByMethod, ResultByMethod, opFixedSuccess, opKnownHttpFailure, opSuccess, opUnknownFailure } from "../operation.js"; -import { LongPollParams, PaginationParams, TalerWireGatewayApi, codecForAddIncomingResponse, codecForIncomingHistory, codecForOutgoingHistory, codecForTransferResponse } from "./types.js"; +import { + FailCasesByMethod, + ResultByMethod, + opFixedSuccess, + opKnownHttpFailure, + opSuccess, + opUnknownFailure, +} from "../operation.js"; +import { + LongPollParams, + PaginationParams, + TalerWireGatewayApi, + codecForAddIncomingResponse, + codecForIncomingHistory, + codecForOutgoingHistory, + codecForTransferResponse, +} from "./types.js"; import { addLongPollingParam, addPaginationParams } from "./utils.js"; -export type TalerWireGatewayResultByMethod<prop extends keyof TalerWireGatewayHttpClient> = ResultByMethod<TalerWireGatewayHttpClient, prop> -export type TalerWireGatewayErrorsByMethod<prop extends keyof TalerWireGatewayHttpClient> = FailCasesByMethod<TalerWireGatewayHttpClient, prop> +export type TalerWireGatewayResultByMethod< + prop extends keyof TalerWireGatewayHttpClient, +> = ResultByMethod<TalerWireGatewayHttpClient, prop>; +export type TalerWireGatewayErrorsByMethod< + prop extends keyof TalerWireGatewayHttpClient, +> = FailCasesByMethod<TalerWireGatewayHttpClient, prop>; /** - * The API is used by the exchange to trigger transactions and query - * incoming transactions, as well as by the auditor to query incoming + * The API is used by the exchange to trigger transactions and query + * incoming transactions, as well as by the auditor to query incoming * and outgoing transactions. - * + * * https://docs.taler.net/core/api-bank-wire.html */ export class TalerWireGatewayHttpClient { @@ -33,7 +68,7 @@ export class TalerWireGatewayHttpClient { // /** // * https://docs.taler.net/core/api-corebank.html#config - // * + // * // */ // async getConfig() { // const url = new URL(`config`, this.baseUrl); @@ -46,110 +81,146 @@ export class TalerWireGatewayHttpClient { // } // } - /** * https://docs.taler.net/core/api-bank-wire.html#post--transfer - * + * */ - async transfer(auth: string, body: TalerWireGatewayApi.TransferRequest, - ) { + async transfer(auth: string, body: TalerWireGatewayApi.TransferRequest) { const url = new URL(`transfer`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader(this.username, auth), }, - body + body, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForTransferResponse()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForTransferResponse()); //FIXME: show more details in docs - case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.BadRequest: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); //FIXME: show more details in docs - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Conflict: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-bank-wire.html#get--history-incoming - * + * */ - async getHistoryIncoming(auth: string, params?: PaginationParams & LongPollParams) { + async getHistoryIncoming( + auth: string, + params?: PaginationParams & LongPollParams, + ) { const url = new URL(`history/incoming`, this.baseUrl); - addPaginationParams(url, params) - addLongPollingParam(url, params) + addPaginationParams(url, params); + addLongPollingParam(url, params); const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { Authorization: makeBasicAuthHeader(this.username, auth), - } + }, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForIncomingHistory()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForIncomingHistory()); //FIXME: account should not be returned or make it optional - case HttpStatusCode.NoContent: return opFixedSuccess({ incoming_transactions: [], credit_account: undefined }) + case HttpStatusCode.NoContent: + return opFixedSuccess(resp, { + incoming_transactions: [], + credit_account: undefined, + }); //FIXME: show more details in docs - case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.BadRequest: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); //FIXME: show more details in docs - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-bank-wire.html#get--history-outgoing - * + * */ - async getHistoryOutgoing(auth: string, params?: PaginationParams & LongPollParams) { + async getHistoryOutgoing( + auth: string, + params?: PaginationParams & LongPollParams, + ) { const url = new URL(`history/outgoing`, this.baseUrl); - addPaginationParams(url, params) - addLongPollingParam(url, params) + addPaginationParams(url, params); + addLongPollingParam(url, params); const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { Authorization: makeBasicAuthHeader(this.username, auth), - } + }, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForOutgoingHistory()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForOutgoingHistory()); //FIXME: account should not be returned or make it optional - case HttpStatusCode.NoContent: return opFixedSuccess({ outgoing_transactions: [], debit_account: undefined }) + case HttpStatusCode.NoContent: + return opFixedSuccess(resp, { + outgoing_transactions: [], + debit_account: undefined, + }); //FIXME: show more details in docs - case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.BadRequest: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); //FIXME: show more details in docs - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-bank-wire.html#post--admin-add-incoming - * + * */ - async addIncoming(auth: string, body: TalerWireGatewayApi.AddIncomingRequest,) { + async addIncoming( + auth: string, + body: TalerWireGatewayApi.AddIncomingRequest, + ) { const url = new URL(`admin/add-incoming`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader(this.username, auth), }, - body + body, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForAddIncomingResponse()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForAddIncomingResponse()); //FIXME: show more details in docs - case HttpStatusCode.BadRequest: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.BadRequest: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); //FIXME: show more details in docs - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Conflict: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } } - diff --git a/packages/taler-util/src/http-client/exchange.ts b/packages/taler-util/src/http-client/exchange.ts index ef2043f40..726c28204 100644 --- a/packages/taler-util/src/http-client/exchange.ts +++ b/packages/taler-util/src/http-client/exchange.ts @@ -3,13 +3,43 @@ import { HttpStatusCode } from "../http-status-codes.js"; import { createPlatformHttpLib } from "../http.js"; import { LibtoolVersion } from "../libtool-version.js"; import { hash } from "../nacl-fast.js"; -import { FailCasesByMethod, ResultByMethod, opEmptySuccess, opFixedSuccess, opKnownHttpFailure, opSuccess, opUnknownFailure } from "../operation.js"; -import { TalerSignaturePurpose, amountToBuffer, bufferForUint32, buildSigPS, decodeCrock, eddsaSign, encodeCrock, stringToBytes, timestampRoundedToBuffer } from "../taler-crypto.js"; -import { OfficerAccount, PaginationParams, SigningKey, TalerExchangeApi, codecForAmlDecisionDetails, codecForAmlRecords, codecForExchangeConfig } from "./types.js"; +import { + FailCasesByMethod, + ResultByMethod, + opEmptySuccess, + opFixedSuccess, + opKnownHttpFailure, + opSuccess, + opUnknownFailure, +} from "../operation.js"; +import { + TalerSignaturePurpose, + amountToBuffer, + bufferForUint32, + buildSigPS, + decodeCrock, + eddsaSign, + encodeCrock, + stringToBytes, + timestampRoundedToBuffer, +} from "../taler-crypto.js"; +import { + OfficerAccount, + PaginationParams, + SigningKey, + TalerExchangeApi, + codecForAmlDecisionDetails, + codecForAmlRecords, + codecForExchangeConfig, +} from "./types.js"; import { addPaginationParams } from "./utils.js"; -export type TalerExchangeResultByMethod<prop extends keyof TalerExchangeHttpClient> = ResultByMethod<TalerExchangeHttpClient, prop> -export type TalerExchangeErrorsByMethod<prop extends keyof TalerExchangeHttpClient> = FailCasesByMethod<TalerExchangeHttpClient, prop> +export type TalerExchangeResultByMethod< + prop extends keyof TalerExchangeHttpClient, +> = ResultByMethod<TalerExchangeHttpClient, prop>; +export type TalerExchangeErrorsByMethod< + prop extends keyof TalerExchangeHttpClient, +> = FailCasesByMethod<TalerExchangeHttpClient, prop>; /** */ @@ -25,61 +55,76 @@ export class TalerExchangeHttpClient { } isCompatible(version: string): boolean { - const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version) - return compare?.compatible ?? false + const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version); + return compare?.compatible ?? false; } /** * https://docs.taler.net/core/api-merchant.html#get--config - * + * */ async getConfig() { const url = new URL(`config`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { - method: "GET" + method: "GET", }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForExchangeConfig()) - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForExchangeConfig()); + default: + return opUnknownFailure(resp, await resp.text()); } } // TERMS - // // AML operations // /** * https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-decisions-$STATE - * + * */ - async getDecisionsByState(auth: OfficerAccount, state: TalerExchangeApi.AmlState, pagination?: PaginationParams) { - const url = new URL(`aml/${auth.id}/decisions/${TalerExchangeApi.AmlState[state]}`, this.baseUrl); - addPaginationParams(url, pagination) + async getDecisionsByState( + auth: OfficerAccount, + state: TalerExchangeApi.AmlState, + pagination?: PaginationParams, + ) { + const url = new URL( + `aml/${auth.id}/decisions/${TalerExchangeApi.AmlState[state]}`, + this.baseUrl, + ); + addPaginationParams(url, pagination); const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { - "Taler-AML-Officer-Signature": buildQuerySignature(auth.signingKey) - } + "Taler-AML-Officer-Signature": buildQuerySignature(auth.signingKey), + }, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForAmlRecords()) - case HttpStatusCode.NoContent: return opFixedSuccess({ records: [] }) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForAmlRecords()); + case HttpStatusCode.NoContent: + return opFixedSuccess(resp, { records: [] }); //this should be unauthorized - case HttpStatusCode.Forbidden: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Forbidden: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Conflict: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-decision-$H_PAYTO - * + * */ async getDecisionDetails(auth: OfficerAccount, account: string) { const url = new URL(`aml/${auth.id}/decision/${account}`, this.baseUrl); @@ -87,48 +132,62 @@ export class TalerExchangeHttpClient { const resp = await this.httpLib.fetch(url.href, { method: "GET", headers: { - "Taler-AML-Officer-Signature": buildQuerySignature(auth.signingKey) - } + "Taler-AML-Officer-Signature": buildQuerySignature(auth.signingKey), + }, }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForAmlDecisionDetails()) - case HttpStatusCode.NoContent: return opFixedSuccess({ aml_history: [], kyc_attributes: [] }) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForAmlDecisionDetails()); + case HttpStatusCode.NoContent: + return opFixedSuccess(resp, { aml_history: [], kyc_attributes: [] }); //this should be unauthorized - case HttpStatusCode.Forbidden: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Forbidden: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Conflict: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } /** * https://docs.taler.net/core/api-exchange.html#post--aml-$OFFICER_PUB-decision - * + * */ - async addDecisionDetails(auth: OfficerAccount, decision: Omit<TalerExchangeApi.AmlDecision, "officer_sig">) { + async addDecisionDetails( + auth: OfficerAccount, + decision: Omit<TalerExchangeApi.AmlDecision, "officer_sig">, + ) { const url = new URL(`aml/${auth.id}/decision`, this.baseUrl); - const body = buildDecisionSignature(auth.signingKey, decision) + const body = buildDecisionSignature(auth.signingKey, decision); const resp = await this.httpLib.fetch(url.href, { method: "POST", body, }); switch (resp.status) { - case HttpStatusCode.NoContent: return opEmptySuccess() + case HttpStatusCode.NoContent: + return opEmptySuccess(resp); //FIXME: this should be unauthorized - case HttpStatusCode.Forbidden: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Forbidden: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Unauthorized: + return opKnownHttpFailure(resp.status, resp); //FIXME: this two need to be split by error code - case HttpStatusCode.NotFound: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Conflict: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); } } - - } function buildQuerySignature(key: SigningKey): string { @@ -143,11 +202,11 @@ function buildDecisionSignature( key: SigningKey, decision: Omit<TalerExchangeApi.AmlDecision, "officer_sig">, ): TalerExchangeApi.AmlDecision { - const zero = new Uint8Array(new ArrayBuffer(64)) + const zero = new Uint8Array(new ArrayBuffer(64)); const sigBlob = buildSigPS(TalerSignaturePurpose.TALER_SIGNATURE_AML_DECISION) //TODO: new need the null terminator, also in the exchange - .put(hash(stringToBytes(decision.justification)))//check null + .put(hash(stringToBytes(decision.justification))) //check null .put(timestampRoundedToBuffer(decision.decision_time)) .put(amountToBuffer(decision.new_threshold)) .put(decodeCrock(decision.h_payto)) @@ -158,6 +217,6 @@ function buildDecisionSignature( const officer_sig = encodeCrock(eddsaSign(sigBlob, key)); return { ...decision, - officer_sig - } -}
\ No newline at end of file + officer_sig, + }; +} diff --git a/packages/taler-util/src/http-client/merchant.ts b/packages/taler-util/src/http-client/merchant.ts index f8d6a4cff..d23bdeb17 100644 --- a/packages/taler-util/src/http-client/merchant.ts +++ b/packages/taler-util/src/http-client/merchant.ts @@ -1,12 +1,37 @@ +/* + This file is part of GNU Taler + (C) 2022-2024 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + import { HttpRequestLibrary } from "../http-common.js"; import { HttpStatusCode } from "../http-status-codes.js"; import { createPlatformHttpLib } from "../http.js"; import { LibtoolVersion } from "../libtool-version.js"; -import { FailCasesByMethod, ResultByMethod, opSuccess, opUnknownFailure } from "../operation.js"; +import { + FailCasesByMethod, + ResultByMethod, + opSuccess, + opUnknownFailure, +} from "../operation.js"; import { codecForMerchantConfig } from "./types.js"; -export type TalerMerchantResultByMethod<prop extends keyof TalerMerchantHttpClient> = ResultByMethod<TalerMerchantHttpClient, prop> -export type TalerMerchantErrorsByMethod<prop extends keyof TalerMerchantHttpClient> = FailCasesByMethod<TalerMerchantHttpClient, prop> +export type TalerMerchantResultByMethod< + prop extends keyof TalerMerchantHttpClient, +> = ResultByMethod<TalerMerchantHttpClient, prop>; +export type TalerMerchantErrorsByMethod< + prop extends keyof TalerMerchantHttpClient, +> = FailCasesByMethod<TalerMerchantHttpClient, prop>; /** */ @@ -22,22 +47,23 @@ export class TalerMerchantHttpClient { } isCompatible(version: string): boolean { - const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version) - return compare?.compatible ?? false + const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version); + return compare?.compatible ?? false; } /** * https://docs.taler.net/core/api-merchant.html#get--config - * + * */ async getConfig() { const url = new URL(`config`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { - method: "GET" + method: "GET", }); switch (resp.status) { - case HttpStatusCode.Ok: return opSuccess(resp, codecForMerchantConfig()) - default: return opUnknownFailure(resp, await resp.text()) + case HttpStatusCode.Ok: + return opSuccess(resp, codecForMerchantConfig()); + default: + return opUnknownFailure(resp, await resp.text()); } } - -}
\ No newline at end of file +} diff --git a/packages/taler-util/src/http-client/officer-account.ts b/packages/taler-util/src/http-client/officer-account.ts index 2c096b651..2c1426be2 100644 --- a/packages/taler-util/src/http-client/officer-account.ts +++ b/packages/taler-util/src/http-client/officer-account.ts @@ -1,3 +1,19 @@ +/* + This file is part of GNU Taler + (C) 2022-2024 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + import { EncryptionNonce, LockedAccount, @@ -12,7 +28,7 @@ import { encryptWithDerivedKey, getRandomBytesF, kdf, - stringToBytes + stringToBytes, } from "@gnu-taler/taler-util"; /** @@ -61,10 +77,10 @@ export async function createNewOfficerAccount( const key = stringToBytes(password); - const localRnd = getRandomBytesF(24) - const mergedRnd: EncryptionNonce = extraNonce ? - kdf(24, stringToBytes("aml-officer"), extraNonce, localRnd) : - localRnd; + const localRnd = getRandomBytesF(24); + const mergedRnd: EncryptionNonce = extraNonce + ? kdf(24, stringToBytes("aml-officer"), extraNonce, localRnd) + : localRnd; const protectedPrivKey = await encryptWithDerivedKey( mergedRnd, diff --git a/packages/taler-util/src/operation.ts b/packages/taler-util/src/operation.ts index 0501de726..213bfeecd 100644 --- a/packages/taler-util/src/operation.ts +++ b/packages/taler-util/src/operation.ts @@ -1,65 +1,137 @@ -import { HttpResponse, readSuccessResponseJsonOrThrow, readTalerErrorResponse } from "./http-common.js"; -import { Codec, HttpStatusCode, TalerError, TalerErrorCode, TalerErrorDetail } from "./index.js"; +/* + This file is part of GNU Taler + (C) 2023-2024 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** + * Imports. + */ +import { + HttpResponse, + readSuccessResponseJsonOrThrow, + readTalerErrorResponse, +} from "./http-common.js"; +import { + Codec, + HttpStatusCode, + TalerError, + TalerErrorCode, + TalerErrorDetail, +} from "./index.js"; export type OperationResult<Body, ErrorEnum> = | OperationOk<Body> | OperationAlternative<ErrorEnum, Body> | OperationFail<ErrorEnum>; -export function isOperationOk<T, E>(c: OperationResult<T, E>): c is OperationOk<T> { - return c.type === "ok" +export function isOperationOk<T, E>( + c: OperationResult<T, E>, +): c is OperationOk<T> { + return c.type === "ok"; } -export function isOperationFail<T, E>(c: OperationResult<T, E>): c is OperationFail<E> { - return c.type === "fail" + +export function isOperationFail<T, E>( + c: OperationResult<T, E>, +): c is OperationFail<E> { + return c.type === "fail"; } /** * successful operation */ -export interface OperationOk<T> { - type: "ok", - body: T; +export interface OperationOk<BodyT> { + type: "ok"; + + httpResp: HttpResponse; + + /** + * Parsed response body. + */ + body: BodyT; } /** * unsuccessful operation, see details */ export interface OperationFail<T> { - type: "fail", - case: T, - detail: TalerErrorDetail, + type: "fail"; + + httpResp: HttpResponse; + + /** + * Error case (either HTTP status code or TalerErrorCode) + */ + case: T; + + detail: TalerErrorDetail; } + /** * unsuccessful operation, see body */ export interface OperationAlternative<T, B> { - type: "fail", - case: T, - body: B, + type: "fail"; + + httpResp: HttpResponse; + + case: T; + body: B; } -export async function opSuccess<T>(resp: HttpResponse, codec: Codec<T>): Promise<OperationOk<T>> { - const body = await readSuccessResponseJsonOrThrow(resp, codec) - return { type: "ok" as const, body } +export async function opSuccess<T>( + resp: HttpResponse, + codec: Codec<T>, +): Promise<OperationOk<T>> { + const body = await readSuccessResponseJsonOrThrow(resp, codec); + return { type: "ok" as const, body, httpResp: resp }; } -export function opFixedSuccess<T>(body: T): OperationOk<T> { - return { type: "ok" as const, body } + +/** + * Success case, but instead of the body we're returning a fixed response + * to the client. + */ +export function opFixedSuccess<T>(resp: HttpResponse, body: T): OperationOk<T> { + return { type: "ok" as const, body, httpResp: resp }; } -export function opEmptySuccess(): OperationOk<void> { - return { type: "ok" as const, body: void 0 } + +export function opEmptySuccess(resp: HttpResponse): OperationOk<void> { + return { type: "ok" as const, body: void 0, httpResp: resp }; } -export async function opKnownAlternativeFailure<T extends HttpStatusCode, B>(resp: HttpResponse, s: T, codec: Codec<B>): Promise<OperationAlternative<T, B>> { - const body = await readSuccessResponseJsonOrThrow(resp, codec) - return { type: "fail", case: s, body } +export async function opKnownAlternativeFailure<T extends HttpStatusCode, B>( + resp: HttpResponse, + s: T, + codec: Codec<B>, +): Promise<OperationAlternative<T, B>> { + const body = await readSuccessResponseJsonOrThrow(resp, codec); + return { type: "fail", case: s, body, httpResp: resp }; } -export async function opKnownHttpFailure<T extends HttpStatusCode>(s: T, resp: HttpResponse): Promise<OperationFail<T>> { - const detail = await readTalerErrorResponse(resp) - return { type: "fail", case: s, detail } + +export async function opKnownHttpFailure<T extends HttpStatusCode>( + s: T, + resp: HttpResponse, +): Promise<OperationFail<T>> { + const detail = await readTalerErrorResponse(resp); + return { type: "fail", case: s, detail, httpResp: resp }; } -export async function opKnownTalerFailure<T extends TalerErrorCode>(s: T, resp: HttpResponse): Promise<OperationFail<T>> { - const detail = await readTalerErrorResponse(resp) - return { type: "fail", case: s, detail } + +export async function opKnownTalerFailure<T extends TalerErrorCode>( + s: T, + resp: HttpResponse, +): Promise<OperationFail<T>> { + const detail = await readTalerErrorResponse(resp); + return { type: "fail", case: s, detail, httpResp: resp }; } export function opUnknownFailure(resp: HttpResponse, text: string): never { @@ -75,11 +147,18 @@ export function opUnknownFailure(resp: HttpResponse, text: string): never { ); } -export type ResultByMethod<TT extends object, p extends keyof TT> = TT[p] extends (...args: any[]) => infer Ret ? - Ret extends Promise<infer Result> ? - Result extends OperationResult<any, any> ? Result : never : - never : //api always use Promises - never; //error cases just for functions - -export type FailCasesByMethod<TT extends object, p extends keyof TT> = Exclude<ResultByMethod<TT, p>, OperationOk<any>> +export type ResultByMethod< + TT extends object, + p extends keyof TT, +> = TT[p] extends (...args: any[]) => infer Ret + ? Ret extends Promise<infer Result> + ? Result extends OperationResult<any, any> + ? Result + : never + : never //api always use Promises + : never; //error cases just for functions +export type FailCasesByMethod<TT extends object, p extends keyof TT> = Exclude< + ResultByMethod<TT, p>, + OperationOk<any> +>; |