aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2023-10-19 02:44:28 -0300
committerSebastian <sebasjm@gmail.com>2023-10-19 02:56:14 -0300
commitf89e27a4e39412c4863fe26f821988a65ecec1b9 (patch)
tree2bc1a5c3971a38fa32268593ccce69f0281858e6
parentc6968c3c21d70bd76ce50f232650d183fa8cfa6e (diff)
downloadwallet-core-f89e27a4e39412c4863fe26f821988a65ecec1b9.tar.xz
update api
-rw-r--r--packages/taler-util/src/http-client/authentication.ts83
-rw-r--r--packages/taler-util/src/http-client/bank-core.ts284
-rw-r--r--packages/taler-util/src/http-client/bank-integration.ts2
-rw-r--r--packages/taler-util/src/http-client/bank-revenue.ts5
-rw-r--r--packages/taler-util/src/http-client/bank-wire.ts8
-rw-r--r--packages/taler-util/src/http-client/types.ts56
-rw-r--r--packages/taler-util/src/http-client/utils.ts110
-rw-r--r--packages/taler-util/src/http-common.ts31
8 files changed, 314 insertions, 265 deletions
diff --git a/packages/taler-util/src/http-client/authentication.ts b/packages/taler-util/src/http-client/authentication.ts
new file mode 100644
index 000000000..0c59c9308
--- /dev/null
+++ b/packages/taler-util/src/http-client/authentication.ts
@@ -0,0 +1,83 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022 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 { HttpStatusCode } from "../http-status-codes.js";
+import { HttpRequestLibrary, createPlatformHttpLib, makeBasicAuthHeader } from "../http.js";
+import { LibtoolVersion } from "../libtool-version.js";
+import { AccessToken, TalerAuthentication, UserAndPassword, UserAndToken, codecForTokenSuccessResponse } from "./types.js";
+import { makeBearerTokenAuthHeader, opEmptySuccess, opKnownFailure, opSuccess, opUnknownFailure } from "./utils.js";
+
+export class TalerAuthenticationHttpClient {
+ public readonly PROTOCOL_VERSION = "0:0:0";
+
+ httpLib: HttpRequestLibrary;
+
+ constructor(
+ readonly baseUrl: string,
+ readonly username: string,
+ httpClient?: HttpRequestLibrary,
+ ) {
+ this.httpLib = httpClient ?? createPlatformHttpLib();
+ }
+
+ isCompatible(version: string): boolean {
+ 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
+ */
+ async createAccessToken(
+ password: string,
+ body: TalerAuthentication.TokenRequest,
+ ) {
+ const url = new URL(`token`, this.baseUrl);
+ const resp = await this.httpLib.fetch(url.href, {
+ method: "POST",
+ headers: {
+ Authorization: makeBasicAuthHeader(this.username, password),
+ },
+ body
+ });
+ switch (resp.status) {
+ case HttpStatusCode.Ok: return opSuccess(resp, codecForTokenSuccessResponse())
+ //FIXME: missing in docs
+ case HttpStatusCode.Unauthorized: return opKnownFailure("wrong-credentials", resp)
+ case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp)
+ default: return opUnknownFailure(resp, await resp.text())
+ }
+ }
+
+ async deleteAccessToken(token: AccessToken) {
+ const url = new URL(`token`, this.baseUrl);
+ const resp = await this.httpLib.fetch(url.href, {
+ method: "DELETE",
+ headers: {
+ Authorization: makeBearerTokenAuthHeader(token),
+ }
+ });
+ switch (resp.status) {
+ case HttpStatusCode.Ok: return opEmptySuccess()
+ //FIXME: missing in docs
+ case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp)
+ default: return opUnknownFailure(resp, await resp.text())
+ }
+ }
+
+} \ No newline at end of file
diff --git a/packages/taler-util/src/http-client/bank-core.ts b/packages/taler-util/src/http-client/bank-core.ts
index 7b4bb53d4..593daa2c3 100644
--- a/packages/taler-util/src/http-client/bank-core.ts
+++ b/packages/taler-util/src/http-client/bank-core.ts
@@ -18,63 +18,50 @@ import {
AmountJson,
Amounts,
HttpStatusCode,
- Logger
+ LibtoolVersion
} from "@gnu-taler/taler-util";
import {
HttpRequestLibrary,
- createPlatformHttpLib,
- expectSuccessResponseOrThrow,
- readSuccessResponseJsonOrThrow
+ createPlatformHttpLib
} from "@gnu-taler/taler-util/http";
import { TalerBankIntegrationHttpClient } from "./bank-integration.js";
import { TalerRevenueHttpClient } from "./bank-revenue.js";
import { TalerWireGatewayHttpClient } from "./bank-wire.js";
-import { AccessToken, TalerAuthentication, TalerCorebankApi, codecForAccountData, codecForBankAccountCreateWithdrawalResponse, codecForBankAccountGetWithdrawalResponse, codecForBankAccountTransactionInfo, codecForBankAccountTransactionsResponse, codecForCashoutConversionResponse, codecForCashoutPending, codecForCashoutStatusResponse, codecForCashouts, codecForConversionRatesResponse, codecForCoreBankConfig, codecForGlobalCashouts, codecForListBankAccountsResponse, codecForMonitorResponse, codecForPublicAccountsResponse, codecForTokenSuccessResponse } from "./types.js";
-import { PaginationParams, UserAndPassword, UserAndToken, addPaginationParams, httpEmptySuccess, httpSuccess, knownFailure, makeBasicAuthHeader, makeBearerTokenAuthHeader, unknownFailure } from "./utils.js";
+import { AccessToken, OperationOk, PaginationParams, TalerCorebankApi, UserAndToken, codecForAccountData, codecForBankAccountCreateWithdrawalResponse, codecForBankAccountGetWithdrawalResponse, codecForBankAccountTransactionInfo, codecForBankAccountTransactionsResponse, codecForCashoutConversionResponse, codecForCashoutPending, codecForCashoutStatusResponse, codecForCashouts, codecForConversionRatesResponse, codecForCoreBankConfig, codecForGlobalCashouts, codecForListBankAccountsResponse, codecForMonitorResponse, codecForPublicAccountsResponse } from "./types.js";
+import { addPaginationParams, opFixedSuccess, opEmptySuccess, opSuccess, opKnownFailure, makeBearerTokenAuthHeader, opUnknownFailure } from "./utils.js";
+import { TalerAuthenticationHttpClient } from "./authentication.js";
-const logger = new Logger("http-client/core-bank.ts");
+type props = keyof TalerCoreBankHttpClient
+
+export type TalerCoreBankResultByMethod<p extends props> = TalerCoreBankHttpClient[p] extends (...args: any[]) => infer Ret ?
+ Ret extends Promise<infer Result> ?
+ Result :
+ never : //api always use Promises
+ never; //error cases just for functions
+
+export type TalerCoreBankErrorsByMethod<p extends props> = Exclude<TalerCoreBankResultByMethod<p>, OperationOk<any>>
+
+/**
+ * Protocol version spoken with the bank.
+ *
+ * Uses libtool's current:revision:age versioning.
+ */
export class TalerCoreBankHttpClient {
+ public readonly PROTOCOL_VERSION = "0:0:0";
+
httpLib: HttpRequestLibrary;
constructor(
- private baseUrl: string,
+ readonly baseUrl: string,
httpClient?: HttpRequestLibrary,
) {
this.httpLib = httpClient ?? createPlatformHttpLib();
}
- /**
- * https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-token
- *
- * @returns
- */
- async createAccessToken(
- auth: UserAndPassword,
- body: TalerAuthentication.TokenRequest,
- ): Promise<TalerAuthentication.TokenSuccessResponse> {
- const url = new URL(`accounts/${auth.username}/token`, this.baseUrl);
- const resp = await this.httpLib.fetch(url.href, {
- method: "POST",
- headers: {
- Authorization: makeBasicAuthHeader(auth.username, auth.password),
- },
- body
- });
- return readSuccessResponseJsonOrThrow(resp, codecForTokenSuccessResponse());
- }
-
- async deleteAccessToken(
- auth: UserAndToken,
- ): Promise<void> {
- const url = new URL(`accounts/${auth.username}/token`, this.baseUrl);
- const resp = await this.httpLib.fetch(url.href, {
- method: "DELETE",
- headers: {
- Authorization: makeBearerTokenAuthHeader(auth.token),
- }
- });
- return expectSuccessResponseOrThrow(resp);
+ isCompatible(version: string): boolean {
+ const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version)
+ return compare?.compatible ?? false
}
/**
@@ -88,8 +75,8 @@ export class TalerCoreBankHttpClient {
});
switch (resp.status) {
//FIXME: missing in docs
- case HttpStatusCode.Ok: return httpSuccess(resp, codecForCoreBankConfig())
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.Ok: return opSuccess(resp, codecForCoreBankConfig())
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -111,17 +98,19 @@ export class TalerCoreBankHttpClient {
},
});
switch (resp.status) {
- case HttpStatusCode.NoContent: return httpEmptySuccess()
- case HttpStatusCode.BadRequest: return knownFailure("invalid-input", resp);
+ //FIXME: NOT IN THE DOOOCS
+ case HttpStatusCode.Created: return opEmptySuccess()
+ case HttpStatusCode.NoContent: return opEmptySuccess()
+ case HttpStatusCode.BadRequest: return opKnownFailure("invalid-input", resp);
case HttpStatusCode.Forbidden: {
if (body.username === "bank" || body.username === "admin") {
- return knownFailure("unable-to-create", resp);
+ return opKnownFailure("unable-to-create", resp);
} else {
- return knownFailure("unauthorized", resp);
+ return opKnownFailure("unauthorized", resp);
}
}
- case HttpStatusCode.Conflict: return knownFailure("already-exist", resp);
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.Conflict: return opKnownFailure("already-exist", resp);
+ default: return opUnknownFailure(resp, await resp.text())
}
}
/**
@@ -137,17 +126,17 @@ export class TalerCoreBankHttpClient {
},
});
switch (resp.status) {
- case HttpStatusCode.NoContent: return httpEmptySuccess()
- case HttpStatusCode.NotFound: return knownFailure("not-found", resp);
+ case HttpStatusCode.NoContent: return opEmptySuccess()
+ case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
case HttpStatusCode.Forbidden: {
if (auth.username === "bank" || auth.username === "admin") {
- return knownFailure("unable-to-delete", resp);
+ return opKnownFailure("unable-to-delete", resp);
} else {
- return knownFailure("unauthorized", resp);
+ return opKnownFailure("unauthorized", resp);
}
}
- case HttpStatusCode.PreconditionFailed: return knownFailure("balance-not-zero", resp);
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.PreconditionFailed: return opKnownFailure("balance-not-zero", resp);
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -165,10 +154,10 @@ export class TalerCoreBankHttpClient {
},
});
switch (resp.status) {
- case HttpStatusCode.NoContent: return httpEmptySuccess()
- case HttpStatusCode.NotFound: return knownFailure("not-found", resp);
- case HttpStatusCode.Forbidden: return knownFailure("unauthorized", resp);
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.NoContent: return opEmptySuccess()
+ case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
+ case HttpStatusCode.Forbidden: return opKnownFailure("unauthorized", resp);
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -186,12 +175,12 @@ export class TalerCoreBankHttpClient {
},
});
switch (resp.status) {
- case HttpStatusCode.NoContent: return httpEmptySuccess()
+ case HttpStatusCode.NoContent: return opEmptySuccess()
//FIXME: missing in docs
- case HttpStatusCode.NotFound: return knownFailure("not-found", resp);
+ case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
//FIXME: missing in docs
- case HttpStatusCode.Forbidden: return knownFailure("unauthorized", resp);
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.Forbidden: return opKnownFailure("unauthorized", resp);
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -199,8 +188,10 @@ export class TalerCoreBankHttpClient {
* https://docs.taler.net/core/get-$BANK_API_BASE_URL-public-accounts
*
*/
- async getPublicAccounts() {
+ async getPublicAccounts(pagination?: PaginationParams) {
const url = new URL(`public-accounts`, this.baseUrl);
+ //FIXME: missing pagination in docs
+ addPaginationParams(url, pagination)
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers: {
@@ -208,10 +199,10 @@ export class TalerCoreBankHttpClient {
});
switch (resp.status) {
//FIXME: missing in docs
- case HttpStatusCode.Ok: return httpSuccess(resp, codecForPublicAccountsResponse())
+ case HttpStatusCode.Ok: return opSuccess(resp, codecForPublicAccountsResponse())
//FIXME: missing in docs
- case HttpStatusCode.NoContent: return httpEmptySuccess()
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.NoContent: return opFixedSuccess({ public_accounts: [] })
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -219,8 +210,9 @@ export class TalerCoreBankHttpClient {
* https://docs.taler.net/core/api-corebank.html#get--accounts
*
*/
- async getAccounts(auth: AccessToken) {
+ async getAccounts(auth: AccessToken, pagination?: PaginationParams) {
const url = new URL(`accounts`, this.baseUrl);
+ addPaginationParams(url, pagination)
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
headers: {
@@ -228,10 +220,10 @@ export class TalerCoreBankHttpClient {
},
});
switch (resp.status) {
- case HttpStatusCode.Ok: return httpSuccess(resp, codecForListBankAccountsResponse())
- case HttpStatusCode.NoContent: return httpEmptySuccess()
- case HttpStatusCode.Forbidden: return knownFailure("unauthorized", resp);
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.Ok: return opSuccess(resp, codecForListBankAccountsResponse())
+ case HttpStatusCode.NoContent: return opFixedSuccess({ accounts: [] })
+ case HttpStatusCode.Forbidden: return opKnownFailure("unauthorized", resp);
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -248,12 +240,12 @@ export class TalerCoreBankHttpClient {
},
});
switch (resp.status) {
- case HttpStatusCode.Ok: return httpSuccess(resp, codecForAccountData())
+ case HttpStatusCode.Ok: return opSuccess(resp, codecForAccountData())
//FIXME: missing in docs
- case HttpStatusCode.NotFound: return knownFailure("not-found", resp);
+ case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
//FIXME: missing in docs
- case HttpStatusCode.Forbidden: return knownFailure("unauthorized", resp);
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.Forbidden: return opKnownFailure("unauthorized", resp);
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -275,14 +267,14 @@ export class TalerCoreBankHttpClient {
},
});
switch (resp.status) {
- case HttpStatusCode.Ok: return httpSuccess(resp, codecForBankAccountTransactionsResponse())
+ case HttpStatusCode.Ok: return opSuccess(resp, codecForBankAccountTransactionsResponse())
//FIXME: missing in docs
- case HttpStatusCode.NoContent: return httpEmptySuccess()
+ case HttpStatusCode.NoContent: return opFixedSuccess({ transactions: [] })
//FIXME: missing in docs
- case HttpStatusCode.NotFound: return knownFailure("not-found", resp);
+ case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
//FIXME: missing in docs
- case HttpStatusCode.Forbidden: return knownFailure("unauthorized", resp);
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.Forbidden: return opKnownFailure("unauthorized", resp);
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -299,12 +291,12 @@ export class TalerCoreBankHttpClient {
},
});
switch (resp.status) {
- case HttpStatusCode.Ok: return httpSuccess(resp, codecForBankAccountTransactionInfo())
+ case HttpStatusCode.Ok: return opSuccess(resp, codecForBankAccountTransactionInfo())
//FIXME: missing in docs
- case HttpStatusCode.NotFound: return knownFailure("not-found", resp);
+ case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
//FIXME: missing in docs
- case HttpStatusCode.Forbidden: return knownFailure("unauthorized", resp);
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.Forbidden: return opKnownFailure("unauthorized", resp);
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -323,12 +315,12 @@ export class TalerCoreBankHttpClient {
});
switch (resp.status) {
//FIXME: fix docs... it should be NoContent
- case HttpStatusCode.Ok: return httpEmptySuccess()
- case HttpStatusCode.NoContent: return httpEmptySuccess()
- case HttpStatusCode.BadRequest: return knownFailure("invalid-input", resp);
+ case HttpStatusCode.Ok: return opEmptySuccess()
+ case HttpStatusCode.NoContent: return opEmptySuccess()
+ case HttpStatusCode.BadRequest: return opKnownFailure("invalid-input", resp);
//FIXME: missing in docs
- case HttpStatusCode.Forbidden: return knownFailure("unauthorized", resp);
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.Forbidden: return opKnownFailure("unauthorized", resp);
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -351,9 +343,9 @@ export class TalerCoreBankHttpClient {
});
switch (resp.status) {
//FIXME: missing in docs
- case HttpStatusCode.Ok: return httpSuccess(resp, codecForBankAccountCreateWithdrawalResponse())
- case HttpStatusCode.Forbidden: return knownFailure("insufficient-funds", resp);
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.Ok: return opSuccess(resp, codecForBankAccountCreateWithdrawalResponse())
+ case HttpStatusCode.Forbidden: return opKnownFailure("insufficient-funds", resp);
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -368,8 +360,9 @@ export class TalerCoreBankHttpClient {
});
switch (resp.status) {
//FIXME: missing in docs
- case HttpStatusCode.Ok: return httpSuccess(resp, codecForBankAccountGetWithdrawalResponse())
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.Ok: return opSuccess(resp, codecForBankAccountGetWithdrawalResponse())
+ case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp)
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -384,10 +377,10 @@ export class TalerCoreBankHttpClient {
});
switch (resp.status) {
//FIXME: fix docs... it should be NoContent
- case HttpStatusCode.Ok: return httpEmptySuccess()
- case HttpStatusCode.NoContent: return httpEmptySuccess()
- case HttpStatusCode.Conflict: return knownFailure("previously-confirmed", resp);
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.Ok: return opEmptySuccess()
+ case HttpStatusCode.NoContent: return opEmptySuccess()
+ case HttpStatusCode.Conflict: return opKnownFailure("previously-confirmed", resp);
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -402,11 +395,11 @@ export class TalerCoreBankHttpClient {
});
switch (resp.status) {
//FIXME: fix docs... it should be NoContent
- case HttpStatusCode.Ok: return httpEmptySuccess()
- case HttpStatusCode.NoContent: return httpEmptySuccess()
- case HttpStatusCode.Conflict: return knownFailure("previously-aborted", resp);
- case HttpStatusCode.UnprocessableEntity: return knownFailure("no-exchange-or-reserve-selected", resp);
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.Ok: return opEmptySuccess()
+ case HttpStatusCode.NoContent: return opEmptySuccess()
+ case HttpStatusCode.Conflict: return opKnownFailure("previously-aborted", resp);
+ case HttpStatusCode.UnprocessableEntity: return opKnownFailure("no-exchange-or-reserve-selected", resp);
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -428,11 +421,17 @@ export class TalerCoreBankHttpClient {
body,
});
switch (resp.status) {
- case HttpStatusCode.Accepted: return httpSuccess(resp, codecForCashoutPending())
+ case HttpStatusCode.Accepted: return opSuccess(resp, codecForCashoutPending())
//FIXME: it should be precondition-failed
- case HttpStatusCode.Conflict: return knownFailure("invalid-state", resp);
- case HttpStatusCode.ServiceUnavailable: return knownFailure("tan-not-supported", resp);
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.Conflict: return opKnownFailure("no-contact-info", resp);
+ //FIXME: missing in the docs
+ case HttpStatusCode.Forbidden: return opKnownFailure("no-allowed", resp);
+ //FIXME: missing in the docs
+ case HttpStatusCode.PreconditionFailed: return opKnownFailure("no-enough-balance", resp);
+ //FIXME: missing in the docs
+ case HttpStatusCode.BadRequest: return opKnownFailure("incorrect-exchange-rate", resp);
+ case HttpStatusCode.ServiceUnavailable: return opKnownFailure("tan-not-supported", resp);
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -449,10 +448,10 @@ export class TalerCoreBankHttpClient {
},
});
switch (resp.status) {
- case HttpStatusCode.NoContent: return httpEmptySuccess()
- case HttpStatusCode.NotFound: return knownFailure("not-found", resp);
- case HttpStatusCode.Conflict: return knownFailure("already-confirmed", resp);
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.NoContent: return opEmptySuccess()
+ case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
+ case HttpStatusCode.Conflict: return opKnownFailure("already-confirmed", resp);
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -470,11 +469,11 @@ export class TalerCoreBankHttpClient {
body,
});
switch (resp.status) {
- case HttpStatusCode.NoContent: return httpEmptySuccess()
- case HttpStatusCode.Forbidden: return knownFailure("wrong-tan-or-credential", resp);
- case HttpStatusCode.NotFound: return knownFailure("not-found", resp);
- case HttpStatusCode.Conflict: return knownFailure("cashout-address-changed", resp);
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.NoContent: return opEmptySuccess()
+ case HttpStatusCode.Forbidden: return opKnownFailure("wrong-tan-or-credential", resp);
+ case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
+ case HttpStatusCode.Conflict: return opKnownFailure("cashout-address-changed", resp);
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -494,10 +493,10 @@ export class TalerCoreBankHttpClient {
method: "GET",
});
switch (resp.status) {
- case HttpStatusCode.Ok: return httpSuccess(resp, codecForCashoutConversionResponse())
- case HttpStatusCode.BadRequest: return knownFailure("wrong-calculation", resp);
- case HttpStatusCode.NotFound: return knownFailure("not-supported", resp);
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.Ok: return opSuccess(resp, codecForCashoutConversionResponse())
+ case HttpStatusCode.BadRequest: return opKnownFailure("wrong-calculation", resp);
+ case HttpStatusCode.NotFound: return opKnownFailure("not-supported", resp);
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -514,9 +513,9 @@ export class TalerCoreBankHttpClient {
},
});
switch (resp.status) {
- case HttpStatusCode.Ok: return httpSuccess(resp, codecForCashouts())
- case HttpStatusCode.NoContent: return httpEmptySuccess();
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.Ok: return opSuccess(resp, codecForCashouts())
+ case HttpStatusCode.NoContent: return opFixedSuccess({ cashouts: [] });
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -533,9 +532,9 @@ export class TalerCoreBankHttpClient {
},
});
switch (resp.status) {
- case HttpStatusCode.Ok: return httpSuccess(resp, codecForGlobalCashouts())
- case HttpStatusCode.NoContent: return httpEmptySuccess();
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.Ok: return opSuccess(resp, codecForGlobalCashouts())
+ case HttpStatusCode.NoContent: return opFixedSuccess({ cashouts: [] });
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -553,9 +552,9 @@ export class TalerCoreBankHttpClient {
});
switch (resp.status) {
//FIXME: missing in docs
- case HttpStatusCode.Ok: return httpSuccess(resp, codecForCashoutStatusResponse())
- case HttpStatusCode.NotFound: return knownFailure("already-aborted", resp);
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.Ok: return opSuccess(resp, codecForCashoutStatusResponse())
+ case HttpStatusCode.NotFound: return opKnownFailure("already-aborted", resp);
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -573,9 +572,9 @@ export class TalerCoreBankHttpClient {
method: "GET",
});
switch (resp.status) {
- case HttpStatusCode.Ok: return httpSuccess(resp, codecForConversionRatesResponse())
- case HttpStatusCode.NotFound: return knownFailure("not-supported", resp);
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.Ok: return opSuccess(resp, codecForConversionRatesResponse())
+ case HttpStatusCode.NotFound: return opKnownFailure("not-supported", resp);
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -595,10 +594,10 @@ export class TalerCoreBankHttpClient {
method: "GET",
});
switch (resp.status) {
- case HttpStatusCode.Ok: return httpSuccess(resp, codecForMonitorResponse())
- case HttpStatusCode.NotFound: return knownFailure("not-supported", resp);
- case HttpStatusCode.BadRequest: return knownFailure("invalid-input", resp);
- default: return unknownFailure(url, resp)
+ case HttpStatusCode.Ok: return opSuccess(resp, codecForMonitorResponse())
+ case HttpStatusCode.NotFound: return opKnownFailure("not-supported", resp);
+ case HttpStatusCode.BadRequest: return opKnownFailure("invalid-input", resp);
+ default: return opUnknownFailure(resp, await resp.text())
}
}
@@ -611,7 +610,7 @@ export class TalerCoreBankHttpClient {
*
*/
getIntegrationAPI(): TalerBankIntegrationHttpClient {
- const url = new URL(`taler-integration`, this.baseUrl);
+ const url = new URL(`taler-integration/`, this.baseUrl);
return new TalerBankIntegrationHttpClient(url.href, this.httpLib)
}
@@ -620,7 +619,7 @@ export class TalerCoreBankHttpClient {
*
*/
getWireGatewayAPI(username: string): TalerWireGatewayHttpClient {
- const url = new URL(`accounts/${username}/taler-wire-gateway`, this.baseUrl);
+ const url = new URL(`accounts/${username}/taler-wire-gateway/`, this.baseUrl);
return new TalerWireGatewayHttpClient(url.href, username, this.httpLib)
}
@@ -629,9 +628,16 @@ export class TalerCoreBankHttpClient {
*
*/
getRevenueAPI(username: string): TalerRevenueHttpClient {
- const url = new URL(`accounts/${username}/taler-revenue`, this.baseUrl);
+ const url = new URL(`accounts/${username}/taler-revenue/`, this.baseUrl);
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,)
+ }
}
-
diff --git a/packages/taler-util/src/http-client/bank-integration.ts b/packages/taler-util/src/http-client/bank-integration.ts
index cd6462417..521b6e34c 100644
--- a/packages/taler-util/src/http-client/bank-integration.ts
+++ b/packages/taler-util/src/http-client/bank-integration.ts
@@ -10,7 +10,7 @@ export class TalerBankIntegrationHttpClient {
httpLib: HttpRequestLibrary;
constructor(
- private baseUrl: string,
+ readonly baseUrl: string,
httpClient?: HttpRequestLibrary,
) {
this.httpLib = httpClient ?? createPlatformHttpLib();
diff --git a/packages/taler-util/src/http-client/bank-revenue.ts b/packages/taler-util/src/http-client/bank-revenue.ts
index 99ff71457..d594da574 100644
--- a/packages/taler-util/src/http-client/bank-revenue.ts
+++ b/packages/taler-util/src/http-client/bank-revenue.ts
@@ -1,14 +1,13 @@
import { HttpRequestLibrary, makeBasicAuthHeader, readSuccessResponseJsonOrThrow } from "../http-common.js";
import { createPlatformHttpLib } from "../http.js";
import { TalerRevenueApi, codecForMerchantIncomingHistory } from "./types.js";
-import { UserAndPassword } from "./utils.js";
export class TalerRevenueHttpClient {
httpLib: HttpRequestLibrary;
constructor(
- private baseUrl: string,
- private username: string,
+ readonly baseUrl: string,
+ readonly username: string,
httpClient?: HttpRequestLibrary,
) {
this.httpLib = httpClient ?? createPlatformHttpLib();
diff --git a/packages/taler-util/src/http-client/bank-wire.ts b/packages/taler-util/src/http-client/bank-wire.ts
index 9f2b859ed..0a032cc61 100644
--- a/packages/taler-util/src/http-client/bank-wire.ts
+++ b/packages/taler-util/src/http-client/bank-wire.ts
@@ -1,7 +1,7 @@
import { HttpRequestLibrary, makeBasicAuthHeader, readSuccessResponseJsonOrThrow } from "../http-common.js";
import { createPlatformHttpLib } from "../http.js";
-import { TalerWireGatewayApi, codecForAddIncomingResponse, codecForIncomingHistory, codecForOutgoingHistory, codecForTransferResponse } from "./types.js";
-import { PaginationParams, UserAndPassword, addPaginationParams } from "./utils.js";
+import { PaginationParams, TalerWireGatewayApi, codecForAddIncomingResponse, codecForIncomingHistory, codecForOutgoingHistory, codecForTransferResponse } from "./types.js";
+import { addPaginationParams } from "./utils.js";
/**
* The API is used by the exchange to trigger transactions and query
@@ -14,8 +14,8 @@ export class TalerWireGatewayHttpClient {
httpLib: HttpRequestLibrary;
constructor(
- private baseUrl: string,
- private username: string,
+ readonly baseUrl: string,
+ readonly username: string,
httpClient?: HttpRequestLibrary,
) {
this.httpLib = httpClient ?? createPlatformHttpLib();
diff --git a/packages/taler-util/src/http-client/types.ts b/packages/taler-util/src/http-client/types.ts
index 66ac39f59..5a76981df 100644
--- a/packages/taler-util/src/http-client/types.ts
+++ b/packages/taler-util/src/http-client/types.ts
@@ -1,6 +1,58 @@
import { codecForAmountString } from "../amounts.js";
import { Codec, buildCodecForObject, buildCodecForUnion, codecForBoolean, codecForConstString, codecForEither, codecForList, codecForMap, codecForNumber, codecForString, codecOptional } from "../codec.js";
import { codecForTimestamp } from "../time.js";
+import { TalerErrorDetail } from "../wallet-types.js";
+
+
+export type UserAndPassword = {
+ username: string,
+ password: string,
+}
+
+export type UserAndToken = {
+ username: string,
+ token: AccessToken,
+}
+
+export type PaginationParams = {
+ /**
+ * row identifier as the starting point of the query
+ */
+ offset?: string,
+ /**
+ * max number of element in the result response
+ * always greater than 0
+ */
+ limit?: number,
+ /**
+ * milliseconds the server should wait for at least one result to be shown
+ */
+ timoutMs?: number,
+ /**
+ * order
+ */
+ order: "asc" | "dec"
+}
+
+export type OperationResult<Body, ErrorEnum> =
+ | OperationOk<Body>
+ | OperationFail<ErrorEnum>;
+
+export interface OperationOk<T> {
+ type: "ok",
+ body: T;
+}
+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 interface OperationFail<T> {
+ type: "fail",
+ case: T,
+ detail: TalerErrorDetail,
+}
///
@@ -502,14 +554,14 @@ export const codecForAddIncomingResponse =
type EmailAddress = string;
type PhoneNumber = string;
-type DecimalNumber = string;
+type DecimalNumber = number;
const codecForURL = codecForString
const codecForLibtoolVersion = codecForString
const codecForCurrencyName = codecForString
const codecForPaytoURI = codecForString
const codecForTalerWithdrawalURI = codecForString
-const codecForDecimalNumber = codecForString
+const codecForDecimalNumber = codecForNumber
enum TanChannel {
SMS = "sms",
diff --git a/packages/taler-util/src/http-client/utils.ts b/packages/taler-util/src/http-client/utils.ts
index f4af5ae03..3be5d6e89 100644
--- a/packages/taler-util/src/http-client/utils.ts
+++ b/packages/taler-util/src/http-client/utils.ts
@@ -1,10 +1,8 @@
import { base64FromArrayBuffer } from "../base64.js";
-import { HttpResponse, readErrorResponse, readSuccessResponseJsonOrThrow, readTalerErrorResponse } from "../http-common.js";
-import { HttpStatusCode } from "../http-status-codes.js";
-import { Codec } from "../index.js";
+import { HttpResponse, readSuccessResponseJsonOrThrow, readTalerErrorResponse } from "../http-common.js";
+import { Codec, TalerError, TalerErrorCode } from "../index.js";
import { stringToBytes } from "../taler-crypto.js";
-import { TalerErrorDetail } from "../wallet-types.js";
-import { AccessToken } from "./types.js";
+import { AccessToken, OperationFail, OperationOk, PaginationParams } from "./types.js";
/**
* Helper function to generate the "Authorization" HTTP header.
@@ -41,91 +39,33 @@ export function addPaginationParams(url: URL, pagination?: PaginationParams) {
url.searchParams.set("delta", String(order * limit))
}
-export type UserAndPassword = {
- username: string,
- password: string,
-}
-
-export type UserAndToken = {
- username: string,
- token: AccessToken,
-}
-export type PaginationParams = {
- /**
- * row identifier as the starting point of the query
- */
- offset?: string,
- /**
- * max number of element in the result response
- * always greater than 0
- */
- limit?: number,
- /**
- * milliseconds the server should wait for at least one result to be shown
- */
- timoutMs?: number,
- /**
- * order
- */
- order: "asc" | "dec"
-}
-
-export type HttpResult<Body, ErrorEnum> =
- | HttpOk<Body>
- | HttpKnownFail<ErrorEnum>
- | HttpUnkownFail;
-
-/**
- * 200 < status < 204
- */
-export interface HttpOk<T> {
- type: "ok",
- body: T;
+//////
+// Operation Helper Constructors
+//////
+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 }
}
-
-/**
- * 400 < status < 409
- * and error documented
- */
-export interface HttpKnownFail<T> {
- type: "fail",
- case: T,
- detail: TalerErrorDetail,
+export function opFixedSuccess<T>(body: T): OperationOk<T> {
+ return { type: "ok" as const, body }
}
-
-/**
- * 400 < status < 599
- * and error NOT documented
- * undefined behavior on this responses
- */
-export interface HttpUnkownFail {
- type: "fail-unknown",
- url: URL;
- status: HttpStatusCode;
-
- // read from the body if exist
- detail?: TalerErrorDetail;
- body?: string;
+export function opEmptySuccess(): OperationOk<void> {
+ return { type: "ok" as const, body: void 0 }
}
-
-export async function knownFailure<T extends string>(s: T, resp: HttpResponse): Promise<HttpKnownFail<T>> {
+export async function opKnownFailure<T extends string>(s: T, resp: HttpResponse): Promise<OperationFail<T>> {
const detail = await readTalerErrorResponse(resp)
return { type: "fail", case: s, detail }
}
-export async function httpSuccess<T>(resp: HttpResponse, codec: Codec<T>): Promise<HttpOk<T>> {
- const body = await readSuccessResponseJsonOrThrow(resp, codec)
- return { type: "ok" as const, body }
-}
-export function httpEmptySuccess(): HttpOk<void> {
- return { type: "ok" as const, body: void 0 }
-}
-export async function unknownFailure(url: URL, resp: HttpResponse): Promise<HttpUnkownFail> {
- if (resp.status >= 400 && resp.status < 500) {
- const detail = await readTalerErrorResponse(resp)
- return { type: "fail-unknown", url, status: resp.status, detail }
- } else {
- const { detail, body } = await readErrorResponse(resp)
- return { type: "fail-unknown", url, status: resp.status, detail, body }
- }
+export function opUnknownFailure(resp: HttpResponse, text: string): never {
+ throw TalerError.fromDetail(
+ TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR,
+ {
+ requestUrl: resp.requestUrl,
+ requestMethod: resp.requestMethod,
+ httpStatusCode: resp.status,
+ errorResponse: text,
+ },
+ `Unexpected HTTP status ${resp.status} in response`,
+ );
}
diff --git a/packages/taler-util/src/http-common.ts b/packages/taler-util/src/http-common.ts
index 817f2367f..da2fbb9da 100644
--- a/packages/taler-util/src/http-common.ts
+++ b/packages/taler-util/src/http-common.ts
@@ -180,37 +180,6 @@ export async function readTalerErrorResponse(
return errJson;
}
-export async function readErrorResponse(
- httpResponse: HttpResponse,
-): Promise<{ detail: TalerErrorDetail | undefined, body: string }> {
- let errString: string;
- try {
- errString = await httpResponse.text();
- } catch (e: any) {
- throw TalerError.fromDetail(
- TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
- {
- requestUrl: httpResponse.requestUrl,
- requestMethod: httpResponse.requestMethod,
- httpStatusCode: httpResponse.status,
- validationError: e.toString(),
- },
- "Couldn't parse JSON format from error response",
- );
- }
- let errJson;
- try {
- errJson = JSON.parse(errString)
- } catch (e) {
- errJson = undefined
- }
-
- const talerErrorCode = errJson && errJson.code;
- if (typeof talerErrorCode === "number") {
- return { detail: errJson, body: errString }
- }
- return { detail: undefined, body: errString };
-}
export async function readUnexpectedResponseDetails(
httpResponse: HttpResponse,
): Promise<TalerErrorDetail> {