diff options
Diffstat (limited to 'packages/demobank-ui/src/hooks/backend.ts')
-rw-r--r-- | packages/demobank-ui/src/hooks/backend.ts | 195 |
1 files changed, 184 insertions, 11 deletions
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 <http://www.gnu.org/licenses/> */ +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: <T>( + path: string, + options?: RequestOptions, + ) => Promise<HttpResponseOk<T>>; + fetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>; + multiFetcher: <T>(endpoint: string[]) => Promise<HttpResponseOk<T>[]>; + paginatedFetcher: <T>(args: [string, number, number]) => Promise<HttpResponseOk<T>>; + sandboxAccountsFetcher: <T>(args: [string, number, number, string]) => Promise<HttpResponseOk<T>>; +} + + +export function usePublicBackend(): useBackendType { + const { state } = useBackendContext(); + const { request: requestHandler } = useApiContext(); + + const baseUrl = state.url + + const request = useCallback( + function requestImpl<T>( + path: string, + options: RequestOptions = {}, + ): Promise<HttpResponseOk<T>> { + + return requestHandler<T>(baseUrl, path, options); + }, + [baseUrl], + ); + + const fetcher = useCallback( + function fetcherImpl<T>(endpoint: string): Promise<HttpResponseOk<T>> { + return requestHandler<T>(baseUrl, endpoint); + }, + [baseUrl], + ); + const paginatedFetcher = useCallback( + function fetcherImpl<T>([endpoint, page, size]: [string, number, number]): Promise<HttpResponseOk<T>> { + return requestHandler<T>(baseUrl, endpoint, { params: { page: page || 1, size } }); + }, + [baseUrl], + ); + const multiFetcher = useCallback( + function multiFetcherImpl<T>( + endpoints: string[], + ): Promise<HttpResponseOk<T>[]> { + return Promise.all( + endpoints.map((endpoint) => requestHandler<T>(baseUrl, endpoint)), + ); + }, + [baseUrl], + ); + const sandboxAccountsFetcher = useCallback( + function fetcherImpl<T>([endpoint, page, size, account]: [string, number, number, string]): Promise<HttpResponseOk<T>> { + return requestHandler<T>(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<T>( + path: string, + options: RequestOptions = {}, + ): Promise<HttpResponseOk<T>> { + + return requestHandler<T>(baseUrl, path, { basicAuth: creds, ...options }); + }, + [baseUrl, creds], + ); + + const fetcher = useCallback( + function fetcherImpl<T>(endpoint: string): Promise<HttpResponseOk<T>> { + return requestHandler<T>(baseUrl, endpoint, { basicAuth: creds }); + }, + [baseUrl, creds], + ); + const paginatedFetcher = useCallback( + function fetcherImpl<T>([endpoint, page = 0, size]: [string, number, number]): Promise<HttpResponseOk<T>> { + return requestHandler<T>(baseUrl, endpoint, { basicAuth: creds, params: { page, size } }); + }, + [baseUrl, creds], + ); + const multiFetcher = useCallback( + function multiFetcherImpl<T>( + endpoints: string[], + ): Promise<HttpResponseOk<T>[]> { + return Promise.all( + endpoints.map((endpoint) => requestHandler<T>(baseUrl, endpoint, { basicAuth: creds })), + ); + }, + [baseUrl, creds], + ); + const sandboxAccountsFetcher = useCallback( + function fetcherImpl<T>([endpoint, page, size, account]: [string, number, number, string]): Promise<HttpResponseOk<T>> { + return requestHandler<T>(baseUrl, endpoint, { basicAuth: creds, params: { page: page || 1, size } }); + }, + [baseUrl], + ); + return { request, fetcher, paginatedFetcher, multiFetcher, sandboxAccountsFetcher }; +} + +export function useBackendConfig(): HttpResponse<SandboxBackend.Config, SandboxBackend.SandboxError> { + const { request } = usePublicBackend(); + + type Type = SandboxBackend.Config; + + const [result, setResult] = useState<HttpResponse<Type, SandboxBackend.SandboxError>>({ loading: true }); + + useEffect(() => { + request<Type>(`/config`) + .then((data) => setResult(data)) + .catch((error) => setResult(error)); + }, [request]); + + return result; +} + +export function useMatchMutate(): ( + re: RegExp, + value?: unknown, +) => Promise<any> { + 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); + }; +} + + |