new libeufin api

This commit is contained in:
Sebastian 2023-09-25 09:31:17 -03:00
parent fd9ed97fdc
commit 0b2c03dc5e
No known key found for this signature in database
GPG Key ID: 173909D1A5F66069
13 changed files with 60 additions and 88 deletions

View File

@ -319,11 +319,10 @@ namespace SandboxBackend {
interface ListBankAccountsResponse {
accounts: AccountMinimalData[];
}
// interface Balance {
// amount: Amount;
// credit_debit_indicator: "credit" | "debit";
// }
type Balance = Amount
interface Balance {
amount: Amount;
credit_debit_indicator: "credit" | "debit";
}
interface AccountMinimalData {
// Username
username: string;

View File

@ -40,6 +40,7 @@ import { useCallback, useEffect, useState } from "preact/hooks";
import { useSWRConfig } from "swr";
import { useBackendContext } from "../context/backend.js";
import { bankUiSettings } from "../settings.js";
import { AccessToken } from "./useCredentialsChecker.js";
/**
* Has the information to reach and
@ -49,7 +50,7 @@ export type BackendState = LoggedIn | LoggedOut;
export interface BackendCredentials {
username: string;
password: string;
token: AccessToken;
}
interface LoggedIn extends BackendCredentials {
@ -64,7 +65,7 @@ export const codecForBackendStateLoggedIn = (): Codec<LoggedIn> =>
buildCodecForObject<LoggedIn>()
.property("status", codecForConstString("loggedIn"))
.property("username", codecForString())
.property("password", codecForString())
.property("token", codecForString() as Codec<AccessToken>)
.property("isUserAdministrator", codecForBoolean())
.build("BackendState.LoggedIn");
@ -255,35 +256,11 @@ interface InvalidationResult {
error: unknown;
}
export function useCredentialsCheckerOld() {
const { request } = useApiContext();
const baseUrl = getInitialBackendBaseURL();
//check against account details endpoint
//while sandbox backend doesn't have a login endpoint
return async function testLogin(
username: string,
password: string,
): Promise<CheckResult> {
try {
await request(baseUrl, `access-api/accounts/${username}/`, {
basicAuth: { username, password },
preventCache: true,
});
return { valid: true };
} catch (error) {
if (error instanceof RequestError) {
return { valid: false, requestError: true, cause: error.cause };
}
return { valid: false, requestError: false, error };
}
};
}
export function useAuthenticatedBackend(): useBackendType {
const { state } = useBackendContext();
const { request: requestHandler } = useApiContext();
const creds = state.status === "loggedIn" ? state : undefined;
const creds = state.status === "loggedIn" ? state.token : undefined;
const baseUrl = getInitialBackendBaseURL();
const request = useCallback(
@ -291,14 +268,14 @@ export function useAuthenticatedBackend(): useBackendType {
path: string,
options: RequestOptions = {},
): Promise<HttpResponseOk<T>> {
return requestHandler<T>(baseUrl, path, { basicAuth: creds, ...options });
return requestHandler<T>(baseUrl, path, { token: creds, ...options });
},
[baseUrl, creds],
);
const fetcher = useCallback(
function fetcherImpl<T>(endpoint: string): Promise<HttpResponseOk<T>> {
return requestHandler<T>(baseUrl, endpoint, { basicAuth: creds });
return requestHandler<T>(baseUrl, endpoint, { token: creds });
},
[baseUrl, creds],
);
@ -309,7 +286,7 @@ export function useAuthenticatedBackend(): useBackendType {
number,
]): Promise<HttpResponseOk<T>> {
return requestHandler<T>(baseUrl, endpoint, {
basicAuth: creds,
token: creds,
params: { delta: size, start: size * page },
});
},
@ -321,7 +298,7 @@ export function useAuthenticatedBackend(): useBackendType {
> {
return Promise.all(
endpoints.map((endpoint) =>
requestHandler<T>(baseUrl, endpoint, { basicAuth: creds }),
requestHandler<T>(baseUrl, endpoint, { token: creds }),
),
);
},
@ -335,7 +312,7 @@ export function useAuthenticatedBackend(): useBackendType {
string,
]): Promise<HttpResponseOk<T>> {
return requestHandler<T>(baseUrl, endpoint, {
basicAuth: creds,
token: creds,
params: { page: page || 1, size },
});
},
@ -347,7 +324,7 @@ export function useAuthenticatedBackend(): useBackendType {
HttpResponseOk<T>
> {
return requestHandler<T>(baseUrl, endpoint, {
basicAuth: creds,
token: creds,
params: { account },
});
},

View File

@ -33,6 +33,7 @@ import {
// FIX default import https://github.com/microsoft/TypeScript/issues/49189
import _useSWR, { SWRHook } from "swr";
import { AmountJson, Amounts } from "@gnu-taler/taler-util";
import { AccessToken } from "./useCredentialsChecker.js";
const useSWR = _useSWR as unknown as SWRHook;
export function useAdminAccountAPI(): AdminAccountAPI {
@ -90,7 +91,8 @@ export function useAdminAccountAPI(): AdminAccountAPI {
await mutateAll(/.*/);
logIn({
username: account,
password: data.new_password,
//FIXME: change password api
token: data.new_password as AccessToken,
});
}
return res;
@ -215,14 +217,15 @@ export interface CircuitAccountAPI {
async function getBusinessStatus(
request: ReturnType<typeof useApiContext>["request"],
basicAuth: { username: string; password: string },
username: string,
token: AccessToken,
): Promise<boolean> {
try {
const url = getInitialBackendBaseURL();
const result = await request<SandboxBackend.Circuit.CircuitAccountData>(
url,
`circuit-api/accounts/${basicAuth.username}`,
{ basicAuth },
`circuit-api/accounts/${username}`,
{ token },
);
return result.ok;
} catch (error) {
@ -264,10 +267,10 @@ type CashoutEstimators = {
export function useEstimator(): CashoutEstimators {
const { state } = useBackendContext();
const { request } = useApiContext();
const basicAuth =
const creds =
state.status === "loggedOut"
? undefined
: { username: state.username, password: state.password };
: state.token;
return {
estimateByCredit: async (amount, fee, rate) => {
const zeroBalance = Amounts.zeroOfCurrency(fee.currency);
@ -282,7 +285,7 @@ export function useEstimator(): CashoutEstimators {
url,
`circuit-api/cashouts/estimates`,
{
basicAuth,
token: creds,
params: {
amount_credit: Amounts.stringify(amount),
},
@ -313,7 +316,7 @@ export function useEstimator(): CashoutEstimators {
url,
`circuit-api/cashouts/estimates`,
{
basicAuth,
token: creds,
params: {
amount_debit: Amounts.stringify(amount),
},
@ -339,11 +342,11 @@ export function useBusinessAccountFlag(): boolean | undefined {
const creds =
state.status === "loggedOut"
? undefined
: { username: state.username, password: state.password };
: {user: state.username, token: state.token};
useEffect(() => {
if (!creds) return;
getBusinessStatus(request, creds)
getBusinessStatus(request, creds.user, creds.token)
.then((result) => {
setIsBusiness(result);
})

View File

@ -23,13 +23,13 @@ export function useCredentialsChecker() {
const response = await request<LoginTokenSuccessResponse>(baseUrl, `accounts/${username}/token`, {
method: "POST",
basicAuth: {
username: username,
username,
password,
},
data,
contentType: "json"
});
return { valid: true, token: response.data.token, expiration: response.data.expiration };
return { valid: true, token: `secret-token:${response.data.access_token}` as AccessToken, expiration: response.data.expiration };
} catch (error) {
if (error instanceof RequestError) {
return { valid: false, cause: error.cause };
@ -76,13 +76,13 @@ export function useCredentialsChecker() {
}
}
return requestNewLoginToken(baseUrl, token.token as AccessToken)
return requestNewLoginToken(baseUrl, token.token)
}
return { requestNewLoginToken, refreshLoginToken }
}
export interface LoginToken {
token: string,
token: AccessToken,
expiration: Timestamp,
}
// token used to get loginToken
@ -95,7 +95,7 @@ export type AccessToken = string & {
type YesOrNo = "yes" | "no";
export type LoginResult = {
valid: true;
token: string;
token: AccessToken;
expiration: Timestamp;
} | {
valid: false;
@ -121,7 +121,7 @@ export interface LoginTokenSuccessResponse {
// that are in scope for some time. Must be prefixed
// with "Bearer " when used in the "Authorization" HTTP header.
// Will already begin with the RFC 8959 prefix.
token: string;
access_token: AccessToken;
// Scope of the token (which kinds of operations it will allow)
scope: "readonly" | "write";

View File

@ -63,9 +63,7 @@ export function useComponentState({ account, goToBusinessAccount, goToConfirmOpe
const { data } = result;
// FIXME: balance
// const balance = Amounts.parseOrThrow(data.balance.amount);
const balance = Amounts.parseOrThrow(data.balance);
const balance = Amounts.parseOrThrow(data.balance.amount);
const debitThreshold = Amounts.parseOrThrow(data.debit_threshold);
const payto = parsePaytoUri(data.payto_uri);

View File

@ -14,16 +14,14 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { Amounts, stringifyPaytoUri } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { Fragment, h, VNode } from "preact";
import { Fragment, VNode, h } from "preact";
import { Transactions } from "../../components/Transactions/index.js";
import { PaymentOptions } from "../PaymentOptions.js";
import { State } from "./index.js";
import { CopyButton } from "../../components/CopyButton.js";
import { bankUiSettings } from "../../settings.js";
import { useBusinessAccountDetails } from "../../hooks/circuit.js";
import { useSettings } from "../../hooks/settings.js";
import { bankUiSettings } from "../../settings.js";
import { PaymentOptions } from "../PaymentOptions.js";
import { State } from "./index.js";
export function InvalidIbanView({ error }: State.InvalidIban) {
return (

View File

@ -474,11 +474,9 @@ function AccountBalance({ account }: { account: string }): VNode {
const result = useAccountDetails(account);
if (!result.ok) return <div />
// FIXME: balance
return <div>
{Amounts.currencyOf(result.data.balance)} {Amounts.stringifyValue(result.data.balance)}
{/* {Amounts.currencyOf(result.data.balance.amount)}
{Amounts.currencyOf(result.data.balance.amount)}
&nbsp;{result.data.balance.credit_debit_indicator === "debit" ? "-" : ""}
{Amounts.stringifyValue(result.data.balance.amount)} */}
{Amounts.stringifyValue(result.data.balance.amount)}
</div>
}

View File

@ -18,13 +18,11 @@ import { HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util";
import { ErrorType, notifyError, useTranslationContext } from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
import { useEffect, useRef, useState } from "preact/hooks";
import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
import { useBackendContext } from "../context/backend.js";
import { useCredentialsChecker } from "../hooks/useCredentialsChecker.js";
import { bankUiSettings } from "../settings.js";
import { undefinedIfEmpty } from "../utils.js";
import { USERNAME_REGEX } from "./RegistrationPage.js";
import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
import { AccessToken, useCredentialsChecker } from "../hooks/useCredentialsChecker.js";
import { useCredentialsCheckerOld } from "../hooks/backend.js";
/**
* Collect and submit login data.
@ -62,7 +60,7 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
setBusy({})
const result = await requestNewLoginToken(username, password);
if (result.valid) {
backend.logIn({ username, password });
backend.logIn({ username, token: result.token });
} else {
const { cause } = result;
switch (cause.type) {

View File

@ -28,6 +28,7 @@ import { bankUiSettings } from "../settings.js";
import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
import { getRandomPassword, getRandomUsername } from "./rnd.js";
import { useCredentialsChecker } from "../hooks/useCredentialsChecker.js";
const logger = new Logger("RegistrationPage");
@ -58,6 +59,7 @@ function RegistrationForm({ onComplete, onCancel }: { onComplete: () => void, on
const [name, setName] = useState<string | undefined>();
const [password, setPassword] = useState<string | undefined>();
const [repeatPassword, setRepeatPassword] = useState<string | undefined>();
const {requestNewLoginToken} = useCredentialsChecker()
const { register } = useTestingAPI();
const { i18n } = useTranslationContext();
@ -83,8 +85,11 @@ function RegistrationForm({ onComplete, onCancel }: { onComplete: () => void, on
if (!username || !password || !name) return;
try {
await register({ name, username, password });
const resp = await requestNewLoginToken(username, password)
setUsername(undefined);
backend.logIn({ username, password });
if (resp.valid) {
backend.logIn({ username, token: resp.token });
}
onComplete();
} catch (error) {
if (error instanceof RequestError) {
@ -125,7 +130,10 @@ function RegistrationForm({ onComplete, onCancel }: { onComplete: () => void, on
setRepeatPassword(undefined);
const username = `_${user.first}-${user.second}_`
await register({ username, name: `${user.first} ${user.second}`, password: pass });
backend.logIn({ username, password: pass });
const resp = await requestNewLoginToken(username, pass)
if (resp.valid) {
backend.logIn({ username, token: resp.token });
}
onComplete();
} catch (error) {
if (error instanceof RequestError) {

View File

@ -17,11 +17,8 @@ export function AdminAccount({ onRegister }: { onRegister: () => void }): VNode
}
const { data } = result;
//FIXME: libeufin does not follow the spec
const balance = Amounts.parseOrThrow(data.balance);
const balanceIsDebit = true;
// const balance = Amounts.parseOrThrow(data.balance.amount);
// const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit";
const balance = Amounts.parseOrThrow(data.balance.amount);
const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit";
const debitThreshold = Amounts.parseOrThrow(result.data.debit_threshold);
const limit = balanceIsDebit

View File

@ -41,9 +41,7 @@ export function RemoveAccount({
if (focus) ref.current?.focus();
}, [focus]);
//FIXME: libeufin does not follow the spec
const balance = Amounts.parse(result.data.balance);
// const balance = Amounts.parse(result.data.balance.amount);
const balance = Amounts.parse(result.data.balance.amount);
if (!balance) {
return <div>there was an error reading the balance</div>;
}

View File

@ -236,11 +236,8 @@ function CreateCashout({
if (!ratiosResult.ok) return onLoadNotOk(ratiosResult);
const config = ratiosResult.data;
//FIXME: libeufin does not follow the spec
const balance = Amounts.parseOrThrow(result.data.balance);
const balanceIsDebit = true;
// const balance = Amounts.parseOrThrow(result.data.balance.amount);
// const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit";
const balance = Amounts.parseOrThrow(result.data.balance.amount);
const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit";
const debitThreshold = Amounts.parseOrThrow(result.data.debit_threshold);
const zero = Amounts.zeroOfCurrency(balance.currency);

View File

@ -26,6 +26,7 @@ import * as pages from "./pages/index.stories.js";
import { ComponentChildren, VNode, h as create } from "preact";
import { BackendStateProviderTesting } from "./context/backend.js";
import { AccessToken } from "./hooks/useCredentialsChecker.js";
setupI18n("en", { en: {} });
@ -56,7 +57,7 @@ function DefaultTestingContext({
state: {
status: "loggedIn",
username: "test",
password: "pwd",
token: "pwd" as AccessToken,
isUserAdministrator: false,
},
});