From a8c5a9696c1735a178158cbc9ac4f9bb4b6f013d Mon Sep 17 00:00:00 2001 From: Sebastian Date: Wed, 8 Feb 2023 17:41:19 -0300 Subject: impl accout management and refactor --- packages/demobank-ui/src/hooks/backend.ts | 195 ++++++++++++++++++++++++++++-- 1 file changed, 184 insertions(+), 11 deletions(-) (limited to 'packages/demobank-ui/src/hooks/backend.ts') diff --git a/packages/demobank-ui/src/hooks/backend.ts b/packages/demobank-ui/src/hooks/backend.ts index 13a158f4f..f4f5ecfd0 100644 --- a/packages/demobank-ui/src/hooks/backend.ts +++ b/packages/demobank-ui/src/hooks/backend.ts @@ -14,7 +14,17 @@ GNU Taler; see the file COPYING. If not, see */ +import { canonicalizeBaseUrl } from "@gnu-taler/taler-util"; import { useLocalStorage } from "@gnu-taler/web-util/lib/index.browser"; +import { + HttpResponse, + HttpResponseOk, + RequestOptions, +} from "@gnu-taler/web-util/lib/index.browser"; +import { useApiContext } from "@gnu-taler/web-util/lib/index.browser"; +import { useCallback, useEffect, useState } from "preact/hooks"; +import { useSWRConfig } from "swr"; +import { useBackendContext } from "../context/backend.js"; /** * Has the information to reach and @@ -22,25 +32,38 @@ import { useLocalStorage } from "@gnu-taler/web-util/lib/index.browser"; */ export type BackendState = LoggedIn | LoggedOut; -export interface BackendInfo { - url: string; +export interface BackendCredentials { username: string; password: string; } -interface LoggedIn extends BackendInfo { +interface LoggedIn extends BackendCredentials { + url: string; status: "loggedIn"; + isUserAdministrator: boolean; } interface LoggedOut { + url: string; status: "loggedOut"; } -export const defaultState: BackendState = { status: "loggedOut" }; +const maybeRootPath = "https://bank.demo.taler.net/demobanks/default/"; + +export function getInitialBackendBaseURL(): string { + const overrideUrl = localStorage.getItem("bank-base-url"); + + return canonicalizeBaseUrl(overrideUrl ? overrideUrl : maybeRootPath); +} + +export const defaultState: BackendState = { + status: "loggedOut", + url: getInitialBackendBaseURL() +}; export interface BackendStateHandler { state: BackendState; - clear(): void; - save(info: BackendInfo): void; + logOut(): void; + logIn(info: BackendCredentials): void; } /** * Return getters and setters for @@ -52,7 +75,7 @@ export function useBackendState(): BackendStateHandler { "backend-state", JSON.stringify(defaultState), ); - // const parsed = value !== undefined ? JSON.parse(value) : value; + let parsed; try { parsed = JSON.parse(value!); @@ -63,12 +86,162 @@ export function useBackendState(): BackendStateHandler { return { state, - clear() { - update(JSON.stringify(defaultState)); + logOut() { + update(JSON.stringify({ ...defaultState, url: state.url })); }, - save(info) { - const nextState: BackendState = { status: "loggedIn", ...info }; + logIn(info) { + //admin is defined by the username + const nextState: BackendState = { status: "loggedIn", url: state.url, ...info, isUserAdministrator: info.username === "admin" }; update(JSON.stringify(nextState)); }, }; } + +interface useBackendType { + request: ( + path: string, + options?: RequestOptions, + ) => Promise>; + fetcher: (endpoint: string) => Promise>; + multiFetcher: (endpoint: string[]) => Promise[]>; + paginatedFetcher: (args: [string, number, number]) => Promise>; + sandboxAccountsFetcher: (args: [string, number, number, string]) => Promise>; +} + + +export function usePublicBackend(): useBackendType { + const { state } = useBackendContext(); + const { request: requestHandler } = useApiContext(); + + const baseUrl = state.url + + const request = useCallback( + function requestImpl( + path: string, + options: RequestOptions = {}, + ): Promise> { + + return requestHandler(baseUrl, path, options); + }, + [baseUrl], + ); + + const fetcher = useCallback( + function fetcherImpl(endpoint: string): Promise> { + return requestHandler(baseUrl, endpoint); + }, + [baseUrl], + ); + const paginatedFetcher = useCallback( + function fetcherImpl([endpoint, page, size]: [string, number, number]): Promise> { + return requestHandler(baseUrl, endpoint, { params: { page: page || 1, size } }); + }, + [baseUrl], + ); + const multiFetcher = useCallback( + function multiFetcherImpl( + endpoints: string[], + ): Promise[]> { + return Promise.all( + endpoints.map((endpoint) => requestHandler(baseUrl, endpoint)), + ); + }, + [baseUrl], + ); + const sandboxAccountsFetcher = useCallback( + function fetcherImpl([endpoint, page, size, account]: [string, number, number, string]): Promise> { + return requestHandler(baseUrl, endpoint, { params: { page: page || 1, size } }); + }, + [baseUrl], + ); + return { request, fetcher, paginatedFetcher, multiFetcher, sandboxAccountsFetcher }; +} + +export function useAuthenticatedBackend(): useBackendType { + const { state } = useBackendContext(); + const { request: requestHandler } = useApiContext(); + + const creds = state.status === "loggedIn" ? state : undefined + const baseUrl = state.url + + const request = useCallback( + function requestImpl( + path: string, + options: RequestOptions = {}, + ): Promise> { + + return requestHandler(baseUrl, path, { basicAuth: creds, ...options }); + }, + [baseUrl, creds], + ); + + const fetcher = useCallback( + function fetcherImpl(endpoint: string): Promise> { + return requestHandler(baseUrl, endpoint, { basicAuth: creds }); + }, + [baseUrl, creds], + ); + const paginatedFetcher = useCallback( + function fetcherImpl([endpoint, page = 0, size]: [string, number, number]): Promise> { + return requestHandler(baseUrl, endpoint, { basicAuth: creds, params: { page, size } }); + }, + [baseUrl, creds], + ); + const multiFetcher = useCallback( + function multiFetcherImpl( + endpoints: string[], + ): Promise[]> { + return Promise.all( + endpoints.map((endpoint) => requestHandler(baseUrl, endpoint, { basicAuth: creds })), + ); + }, + [baseUrl, creds], + ); + const sandboxAccountsFetcher = useCallback( + function fetcherImpl([endpoint, page, size, account]: [string, number, number, string]): Promise> { + return requestHandler(baseUrl, endpoint, { basicAuth: creds, params: { page: page || 1, size } }); + }, + [baseUrl], + ); + return { request, fetcher, paginatedFetcher, multiFetcher, sandboxAccountsFetcher }; +} + +export function useBackendConfig(): HttpResponse { + const { request } = usePublicBackend(); + + type Type = SandboxBackend.Config; + + const [result, setResult] = useState>({ loading: true }); + + useEffect(() => { + request(`/config`) + .then((data) => setResult(data)) + .catch((error) => setResult(error)); + }, [request]); + + return result; +} + +export function useMatchMutate(): ( + re: RegExp, + value?: unknown, +) => Promise { + const { cache, mutate } = useSWRConfig(); + + if (!(cache instanceof Map)) { + throw new Error( + "matchMutate requires the cache provider to be a Map instance", + ); + } + + return function matchRegexMutate(re: RegExp, value?: unknown) { + const allKeys = Array.from(cache.keys()); + const keys = allKeys.filter((key) => re.test(key)); + const mutations = keys.map((key) => { + mutate(key, value, true); + }); + return Promise.all(mutations); + }; +} + + -- cgit v1.2.3