commit e0d682cd27402a5cc464779a621951528f32c3e9 parent 9c6d04fcbfa53330c79bd4ab2ad4729c20d6a09c Author: Florian Dold <florian@dold.me> Date: Mon, 8 Jul 2024 15:36:00 +0200 util: more API type decl reorg Diffstat:
15 files changed, 232 insertions(+), 236 deletions(-)
diff --git a/packages/challenger-ui/src/pages/AskChallenge.tsx b/packages/challenger-ui/src/pages/AskChallenge.tsx @@ -288,7 +288,7 @@ export function AskChallenge({ > <div class="sm:col-span-2"> <label - for="adress" + for="address" class="block text-sm font-semibold leading-6 text-gray-900" > {(function (): TranslatedString { @@ -303,8 +303,8 @@ export function AskChallenge({ <div class="mt-2.5"> <input type="text" - name="adress" - id="adress" + name="address" + id="address" ref={focus ? doAutoFocus : undefined} maxLength={512} autocomplete={(function (): string { diff --git a/packages/taler-harness/src/integrationtests/test-merchant-categories.ts b/packages/taler-harness/src/integrationtests/test-merchant-categories.ts @@ -130,7 +130,7 @@ export async function runMerchantCategoriesTest(t: GlobalTestState) { price: "TESTKUDOS:2", total_stock: -1, // FIXME: Don't hardcode - catgories: [myNewCategoryId], + categories: [myNewCategoryId], }, }); t.assertTrue(res.status >= 200 && res.status < 300); @@ -147,12 +147,12 @@ export async function runMerchantCategoriesTest(t: GlobalTestState) { t.assertDeepEqual(posJson.products.length, 2); - const prodFoo = posJson.products.find((x: any) => x.product_id = "foo"); + const prodFoo = posJson.products.find((x: any) => (x.product_id = "foo")); t.assertTrue(!!prodFoo); // Only default category t.assertDeepEqual(prodFoo.categories, [0]); - const prodBar = posJson.products.find((x: any) => x.product_id = "bar"); + const prodBar = posJson.products.find((x: any) => (x.product_id = "bar")); t.assertTrue(!!prodBar); // This should have the one we assigned to it. t.assertDeepEqual(prodBar.categories, [myNewCategoryId]); diff --git a/packages/taler-util/src/MerchantApiClient.ts b/packages/taler-util/src/MerchantApiClient.ts @@ -15,7 +15,6 @@ */ import { codecForAny } from "./codec.js"; -import { TalerMerchantApi } from "./http-client/types.js"; import { HttpStatusCode } from "./http-status-codes.js"; import { createPlatformHttpLib, @@ -38,13 +37,14 @@ import { import { TalerProtocolDuration } from "./time.js"; import { AmountString } from "./types-taler-common.js"; import { - FacadeCredentials, OtpDeviceAddDetails, codecForMerchantConfig, codecForMerchantOrderPrivateStatusResponse, codecForPostOrderResponse, } from "./types-taler-merchant.js"; +import * as TalerMerchantApi from "./types-taler-merchant.js"; + const logger = new Logger("MerchantApiClient.ts"); // FIXME: Explain! diff --git a/packages/taler-util/src/http-client/authentication.ts b/packages/taler-util/src/http-client/authentication.ts @@ -33,10 +33,10 @@ import { } from "../operation.js"; import { AccessToken, - TalerAuthentication, + TokenRequest, codecForTokenSuccessResponse, codecForTokenSuccessResponseMerchant, -} from "./types.js"; +} from "../types-taler-common.js"; import { makeBearerTokenAuthHeader } from "./utils.js"; export class TalerAuthenticationHttpClient { @@ -64,7 +64,7 @@ export class TalerAuthenticationHttpClient { async createAccessTokenBasic( username: string, password: string, - body: TalerAuthentication.TokenRequest, + body: TokenRequest, ) { const url = new URL(`token`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { @@ -91,10 +91,7 @@ export class TalerAuthenticationHttpClient { * * @returns */ - async createAccessTokenBearer( - token: AccessToken, - body: TalerAuthentication.TokenRequest, - ) { + async createAccessTokenBearer(token: AccessToken, body: TokenRequest) { const url = new URL(`token`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "POST", diff --git a/packages/taler-util/src/http-client/bank-conversion.ts b/packages/taler-util/src/http-client/bank-conversion.ts @@ -32,12 +32,13 @@ import { } from "../operation.js"; import { TalerErrorCode } from "../taler-error-codes.js"; import { + ConversionRate, codecForCashinConversionResponse, codecForCashoutConversionResponse, codecForConversionBankConfig, } from "../types-taler-bank-conversion.js"; +import { AccessToken } from "../types-taler-common.js"; import { codecForTalerErrorDetail } from "../types-taler-wallet.js"; -import { AccessToken, TalerBankConversionApi } from "./types.js"; import { CacheEvictor, makeBearerTokenAuthHeader, @@ -192,10 +193,7 @@ export class TalerBankConversionHttpClient { * 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: ConversionRate) { const url = new URL(`conversion-rate`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { method: "POST", diff --git a/packages/taler-util/src/http-client/bank-core.ts b/packages/taler-util/src/http-client/bank-core.ts @@ -16,14 +16,16 @@ import { AbsoluteTime, + AccessToken, HttpStatusCode, LibtoolVersion, - LibtoolVersionString, LongPollParams, OperationAlternative, OperationFail, OperationOk, + PaginationParams, TalerErrorCode, + UserAndToken, opKnownAlternativeFailure, opKnownHttpFailure, opKnownTalerFailure, @@ -62,12 +64,6 @@ import { codecForWithdrawalPublicInfo, } from "../types-taler-corebank.js"; import { - AccessToken, - PaginationParams, - TalerCorebankApi, - UserAndToken, -} from "./types.js"; -import { CacheEvictor, IdempotencyRetry, addLongPollingParam, @@ -76,6 +72,8 @@ import { nullEvictor, } from "./utils.js"; +import * as TalerCorebankApi from "../types-taler-corebank.js"; + export type TalerCoreBankResultByMethod< prop extends keyof TalerCoreBankHttpClient, > = ResultByMethod<TalerCoreBankHttpClient, prop>; diff --git a/packages/taler-util/src/http-client/bank-integration.ts b/packages/taler-util/src/http-client/bank-integration.ts @@ -30,13 +30,14 @@ import { } from "../operation.js"; import { TalerErrorCode } from "../taler-error-codes.js"; import { + BankWithdrawalOperationPostRequest, WithdrawalOperationStatus, codecForBankWithdrawalOperationPostResponse, codecForBankWithdrawalOperationStatus, } from "../types-taler-bank-integration.js"; +import { LongPollParams } from "../types-taler-common.js"; import { codecForIntegrationBankConfig } from "../types-taler-corebank.js"; import { codecForTalerErrorDetail } from "../types-taler-wallet.js"; -import { LongPollParams, TalerBankIntegrationApi } from "./types.js"; import { addLongPollingParam } from "./utils.js"; export type TalerBankIntegrationResultByMethod< @@ -125,7 +126,7 @@ export class TalerBankIntegrationHttpClient { */ async completeWithdrawalOperationById( woid: string, - body: TalerBankIntegrationApi.BankWithdrawalOperationPostRequest, + body: BankWithdrawalOperationPostRequest, ) { const url = new URL(`withdrawal-operation/${woid}`, this.baseUrl); const resp = await this.httpLib.fetch(url.href, { diff --git a/packages/taler-util/src/http-client/bank-revenue.ts b/packages/taler-util/src/http-client/bank-revenue.ts @@ -30,11 +30,11 @@ import { opSuccessFromHttp, opUnknownFailure, } from "../operation.js"; +import { LongPollParams, PaginationParams } from "../types-taler-common.js"; import { codecForRevenueConfig, codecForRevenueIncomingHistory, } from "../types-taler-revenue.js"; -import { LongPollParams, PaginationParams } from "./types.js"; import { addLongPollingParam, addPaginationParams } from "./utils.js"; export type TalerBankRevenueResultByMethod< diff --git a/packages/taler-util/src/http-client/bank-wire.ts b/packages/taler-util/src/http-client/bank-wire.ts @@ -35,13 +35,11 @@ import { codecForOutgoingHistory, codecForTransferResponse, } from "../types-taler-wire-gateway.js"; -import { - LongPollParams, - PaginationParams, - TalerWireGatewayApi, -} from "./types.js"; import { addLongPollingParam, addPaginationParams } from "./utils.js"; +import { LongPollParams, PaginationParams } from "../types-taler-common.js"; +import * as TalerWireGatewayApi from "../types-taler-wire-gateway.js"; + export type TalerWireGatewayResultByMethod< prop extends keyof TalerWireGatewayHttpClient, > = ResultByMethod<TalerWireGatewayHttpClient, prop>; diff --git a/packages/taler-util/src/http-client/challenger.ts b/packages/taler-util/src/http-client/challenger.ts @@ -1,3 +1,24 @@ +/* + This file is part of GNU Taler + (C) 2024 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero 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 Affero General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + + SPDX-License-Identifier: AGPL-3.0-or-later + */ + +/** + * Imports. + */ import { HttpRequestLibrary, readTalerErrorResponse } from "../http-common.js"; import { HttpStatusCode } from "../http-status-codes.js"; import { createPlatformHttpLib } from "../http.js"; @@ -20,7 +41,7 @@ import { codecForChallengerInfoResponse, codecForChallengerTermsOfServiceResponse, } from "../types-taler-challenger.js"; -import { AccessToken } from "./types.js"; +import { AccessToken } from "../types-taler-common.js"; import { CacheEvictor, makeBearerTokenAuthHeader, diff --git a/packages/taler-util/src/http-client/exchange.ts b/packages/taler-util/src/http-client/exchange.ts @@ -24,19 +24,20 @@ import { timestampRoundedToBuffer, } from "../taler-crypto.js"; import { + OfficerAccount, + PaginationParams, + SigningKey, +} from "../types-taler-common.js"; +import { codecForAmlDecisionDetails, codecForAmlRecords, codecForExchangeConfig, codecForExchangeKeys, } from "../types-taler-exchange.js"; -import { - OfficerAccount, - PaginationParams, - SigningKey, - TalerExchangeApi, -} from "./types.js"; import { CacheEvictor, addPaginationParams, nullEvictor } from "./utils.js"; +import * as TalerExchangeApi from "../types-taler-exchange.js"; + export type TalerExchangeResultByMethod< prop extends keyof TalerExchangeHttpClient, > = ResultByMethod<TalerExchangeHttpClient, prop>; diff --git a/packages/taler-util/src/http-client/types.ts b/packages/taler-util/src/http-client/types.ts @@ -1,187 +0,0 @@ -import { Codec, buildCodecForObject, codecForString } from "../codec.js"; -import { codecForTimestamp } from "../time.js"; -import { RelativeTime, Timestamp } from "../types-taler-common.js"; - -export type UserAndPassword = { - username: string; - password: string; -}; - -export type UserAndToken = { - username: string; - token: AccessToken; -}; - -declare const opaque_OfficerAccount: unique symbol; -export type LockedAccount = string & { [opaque_OfficerAccount]: true }; - -declare const opaque_OfficerId: unique symbol; -export type OfficerId = string & { [opaque_OfficerId]: true }; - -declare const opaque_OfficerSigningKey: unique symbol; -export type SigningKey = Uint8Array & { [opaque_OfficerSigningKey]: true }; - -export interface OfficerAccount { - id: OfficerId; - signingKey: SigningKey; -} - -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; - /** - * order - */ - order?: "asc" | "dec"; -}; - -export type LongPollParams = { - /** - * milliseconds the server should wait for at least one result to be shown - */ - timeoutMs?: number; -}; -/// -/// HASH -/// - -export interface LoginToken { - token: AccessToken; - expiration: Timestamp; -} - -declare const __ac_token: unique symbol; -/** - * Use `createAccessToken(string)` function to build one. - */ -export type AccessToken = string & { - [__ac_token]: true; -}; - -/** - * Create a rfc8959 access token. - * Adds secret-token: prefix if there is none. - * Encode the token with rfc7230 to send in a http header. - * - * @param token - * @returns - */ -export function createRFC8959AccessTokenEncoded(token: string): AccessToken { - return ( - token.startsWith("secret-token:") - ? token - : `secret-token:${encodeURIComponent(token)}` - ) as AccessToken; -} - -/** - * Create a rfc8959 access token. - * Adds secret-token: prefix if there is none. - * - * @param token - * @returns - */ -export function createRFC8959AccessTokenPlain(token: string): AccessToken { - return ( - token.startsWith("secret-token:") ? token : `secret-token:${token}` - ) as AccessToken; -} - -/** - * Convert string to access token. - * - * @param clientSecret - * @returns - */ -export function createClientSecretAccessToken( - clientSecret: string, -): AccessToken { - return clientSecret as AccessToken; -} - -declare const __officer_signature: unique symbol; -export type OfficerSignature = string & { - [__officer_signature]: true; -}; - -export namespace TalerAuthentication { - export interface TokenRequest { - // Service-defined scope for the token. - // Typical scopes would be "readonly" or "readwrite". - scope: string; - - // Server may impose its own upper bound - // on the token validity duration - duration?: RelativeTime; - - // Is the token refreshable into a new token during its - // validity? - // Refreshable tokens effectively provide indefinite - // access if they are refreshed in time. - refreshable?: boolean; - } - - export interface TokenSuccessResponse { - // Expiration determined by the server. - // Can be based on the token_duration - // from the request, but ultimately the - // server decides the expiration. - expiration: Timestamp; - - // Opque access token. - access_token: AccessToken; - } - export interface TokenSuccessResponseMerchant { - // Expiration determined by the server. - // Can be based on the token_duration - // from the request, but ultimately the - // server decides the expiration. - expiration: Timestamp; - - // Opque access token. - token: AccessToken; - } -} - -//FIXME: implement this codec -export const codecForAccessToken = codecForString as () => Codec<AccessToken>; -export const codecForTokenSuccessResponse = - (): Codec<TalerAuthentication.TokenSuccessResponse> => - buildCodecForObject<TalerAuthentication.TokenSuccessResponse>() - .property("access_token", codecForAccessToken()) - .property("expiration", codecForTimestamp) - .build("TalerAuthentication.TokenSuccessResponse"); - -export const codecForTokenSuccessResponseMerchant = - (): Codec<TalerAuthentication.TokenSuccessResponseMerchant> => - buildCodecForObject<TalerAuthentication.TokenSuccessResponseMerchant>() - .property("token", codecForAccessToken()) - .property("expiration", codecForTimestamp) - .build("TalerAuthentication.TokenSuccessResponseMerchant"); - -//FIXME: implement this codec -export const codecForURN = codecForString; - -export * as TalerWireGatewayApi from "../types-taler-wire-gateway.js"; - -export * as TalerBankIntegrationApi from "../types-taler-bank-integration.js"; - -export * as TalerBankConversionApi from "../types-taler-bank-conversion.js"; - -export * as TalerCorebankApi from "../types-taler-corebank.js"; - -export * as TalerExchangeApi from "../types-taler-exchange.js"; - -export * as TalerRevenueApi from "../types-taler-revenue.js"; - -export * as TalerMerchantApi from "../types-taler-merchant.js"; - -// Deprecated alias, for easier refactoring! -export * as ChallengerApi from "../types-taler-challenger.js"; diff --git a/packages/taler-util/src/http-client/utils.ts b/packages/taler-util/src/http-client/utils.ts @@ -19,7 +19,11 @@ */ import { base64FromArrayBuffer } from "../base64.js"; import { encodeCrock, getRandomBytes, stringToBytes } from "../taler-crypto.js"; -import { AccessToken, LongPollParams, PaginationParams } from "./types.js"; +import { + AccessToken, + LongPollParams, + PaginationParams, +} from "../types-taler-common.js"; /** * Helper function to generate the "Authorization" HTTP header. @@ -99,17 +103,17 @@ export class IdempotencyRetry { private constructor(timesLeft: number, maxTimesLeft: number) { this.timesLeft = timesLeft; this.maxTries = maxTimesLeft; - this.uid = encodeCrock(getRandomBytes(32)) + this.uid = encodeCrock(getRandomBytes(32)); } - + static tryFiveTimes() { - return new IdempotencyRetry(5, 5) + return new IdempotencyRetry(5, 5); } next(): IdempotencyRetry | undefined { - const left = this.timesLeft -1 + const left = this.timesLeft - 1; if (left <= 0) { - return undefined + return undefined; } return new IdempotencyRetry(left, this.maxTries); } diff --git a/packages/taler-util/src/index.ts b/packages/taler-util/src/index.ts @@ -27,7 +27,6 @@ export * from "./http-client/challenger.js"; export * from "./http-client/exchange.js"; export * from "./http-client/merchant.js"; export * from "./http-client/officer-account.js"; -export * from "./http-client/types.js"; export { CacheEvictor } from "./http-client/utils.js"; export * from "./http-status-codes.js"; export * from "./i18n.js"; @@ -66,3 +65,12 @@ export * from "./types-taler-merchant.js"; export * from "./types-taler-sync.js"; export * from "./types-taler-wallet-transactions.js"; export * from "./types-taler-wallet.js"; + +export * as TalerBankConversionApi from "./types-taler-bank-conversion.js"; +export * as TalerBankIntegrationApi from "./types-taler-bank-integration.js"; +export * as ChallengerApi from "./types-taler-challenger.js"; +export * as TalerCorebankApi from "./types-taler-corebank.js"; +export * as TalerExchangeApi from "./types-taler-exchange.js"; +export * as TalerMerchantApi from "./types-taler-merchant.js"; +export * as TalerRevenueApi from "./types-taler-revenue.js"; +export * as TalerWireGatewayApi from "./types-taler-wire-gateway.js"; diff --git a/packages/taler-util/src/types-taler-common.ts b/packages/taler-util/src/types-taler-common.ts @@ -403,3 +403,160 @@ export const codecForCoinHistoryResponse = () => .property("h_denom_pub", codecForString()) .property("history", codecForAny()) .build("CoinHistoryResponse"); + +export interface TokenRequest { + // Service-defined scope for the token. + // Typical scopes would be "readonly" or "readwrite". + scope: string; + + // Server may impose its own upper bound + // on the token validity duration + duration?: RelativeTime; + + // Is the token refreshable into a new token during its + // validity? + // Refreshable tokens effectively provide indefinite + // access if they are refreshed in time. + refreshable?: boolean; +} + +export interface TokenSuccessResponse { + // Expiration determined by the server. + // Can be based on the token_duration + // from the request, but ultimately the + // server decides the expiration. + expiration: Timestamp; + + // Opque access token. + access_token: AccessToken; +} +export interface TokenSuccessResponseMerchant { + // Expiration determined by the server. + // Can be based on the token_duration + // from the request, but ultimately the + // server decides the expiration. + expiration: Timestamp; + + // Opque access token. + token: AccessToken; +} + +//FIXME: implement this codec +export const codecForAccessToken = codecForString as () => Codec<AccessToken>; +export const codecForTokenSuccessResponse = (): Codec<TokenSuccessResponse> => + buildCodecForObject<TokenSuccessResponse>() + .property("access_token", codecForAccessToken()) + .property("expiration", codecForTimestamp) + .build("TalerAuthentication.TokenSuccessResponse"); + +export const codecForTokenSuccessResponseMerchant = + (): Codec<TokenSuccessResponseMerchant> => + buildCodecForObject<TokenSuccessResponseMerchant>() + .property("token", codecForAccessToken()) + .property("expiration", codecForTimestamp) + .build("TalerAuthentication.TokenSuccessResponseMerchant"); + +// FIXME: implement this codec +export const codecForURN = codecForString; + +declare const __ac_token: unique symbol; + +/** + * Use `createAccessToken(string)` function to build one. + */ +export type AccessToken = string & { + [__ac_token]: true; +}; + +/** + * Create a rfc8959 access token. + * Adds secret-token: prefix if there is none. + * Encode the token with rfc7230 to send in a http header. + * + * @param token + * @returns + */ +export function createRFC8959AccessTokenEncoded(token: string): AccessToken { + return ( + token.startsWith("secret-token:") + ? token + : `secret-token:${encodeURIComponent(token)}` + ) as AccessToken; +} + +/** + * Create a rfc8959 access token. + * Adds secret-token: prefix if there is none. + * + * @param token + * @returns + */ +export function createRFC8959AccessTokenPlain(token: string): AccessToken { + return ( + token.startsWith("secret-token:") ? token : `secret-token:${token}` + ) as AccessToken; +} + +/** + * Convert string to access token. + * + * @param clientSecret + * @returns + */ +export function createClientSecretAccessToken( + clientSecret: string, +): AccessToken { + return clientSecret as AccessToken; +} + +export type UserAndPassword = { + username: string; + password: string; +}; + +export type UserAndToken = { + username: string; + token: AccessToken; +}; + +declare const opaque_OfficerAccount: unique symbol; +export type LockedAccount = string & { [opaque_OfficerAccount]: true }; + +declare const opaque_OfficerId: unique symbol; +export type OfficerId = string & { [opaque_OfficerId]: true }; + +declare const opaque_OfficerSigningKey: unique symbol; +export type SigningKey = Uint8Array & { [opaque_OfficerSigningKey]: true }; + +export interface OfficerAccount { + id: OfficerId; + signingKey: SigningKey; +} + +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; + /** + * order + */ + order?: "asc" | "dec"; +}; + +export type LongPollParams = { + /** + * milliseconds the server should wait for at least one result to be shown + */ + timeoutMs?: number; +}; + +export interface LoginToken { + token: AccessToken; + expiration: Timestamp; +}