aboutsummaryrefslogtreecommitdiff
path: root/packages/demobank-ui/src/hooks/backend.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/demobank-ui/src/hooks/backend.ts')
-rw-r--r--packages/demobank-ui/src/hooks/backend.ts195
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);
+ };
+}
+
+