summaryrefslogtreecommitdiff
path: root/packages/merchant-backoffice-ui/src/hooks/backend.ts
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2022-10-24 10:46:14 +0200
committerFlorian Dold <florian@dold.me>2022-10-24 10:46:14 +0200
commit3e060b80428943c6562250a6ff77eff10a0259b7 (patch)
treed08472bc5ca28621c62ac45b229207d8215a9ea7 /packages/merchant-backoffice-ui/src/hooks/backend.ts
parentfb52ced35ac872349b0e1062532313662552ff6c (diff)
downloadwallet-core-3e060b80428943c6562250a6ff77eff10a0259b7.tar.gz
wallet-core-3e060b80428943c6562250a6ff77eff10a0259b7.tar.bz2
wallet-core-3e060b80428943c6562250a6ff77eff10a0259b7.zip
repo: integrate packages from former merchant-backoffice.git
Diffstat (limited to 'packages/merchant-backoffice-ui/src/hooks/backend.ts')
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/backend.ts319
1 files changed, 319 insertions, 0 deletions
diff --git a/packages/merchant-backoffice-ui/src/hooks/backend.ts b/packages/merchant-backoffice-ui/src/hooks/backend.ts
new file mode 100644
index 000000000..789cfc81c
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/hooks/backend.ts
@@ -0,0 +1,319 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { useSWRConfig } from "swr";
+import axios, { AxiosError, AxiosResponse } from "axios";
+import { MerchantBackend } from "../declaration";
+import { useBackendContext } from "../context/backend";
+import { useEffect, useState } from "preact/hooks";
+import { DEFAULT_REQUEST_TIMEOUT } from "../utils/constants";
+import { axiosHandler, removeAxiosCancelToken } from "../utils/switchableAxios";
+
+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());
+ // console.log(allKeys)
+ const keys = allKeys.filter((key) => re.test(key));
+ // console.log(allKeys.length, keys.length)
+ const mutations = keys.map((key) => {
+ // console.log(key)
+ mutate(key, value, true);
+ });
+ return Promise.all(mutations);
+ };
+}
+
+export type HttpResponse<T> =
+ | HttpResponseOk<T>
+ | HttpResponseLoading<T>
+ | HttpError;
+export type HttpResponsePaginated<T> =
+ | HttpResponseOkPaginated<T>
+ | HttpResponseLoading<T>
+ | HttpError;
+
+export interface RequestInfo {
+ url: string;
+ hasToken: boolean;
+ params: unknown;
+ data: unknown;
+ status: number;
+}
+
+interface HttpResponseLoading<T> {
+ ok?: false;
+ loading: true;
+ clientError?: false;
+ serverError?: false;
+
+ data?: T;
+}
+export interface HttpResponseOk<T> {
+ ok: true;
+ loading?: false;
+ clientError?: false;
+ serverError?: false;
+
+ data: T;
+ info?: RequestInfo;
+}
+
+export type HttpResponseOkPaginated<T> = HttpResponseOk<T> & WithPagination;
+
+export interface WithPagination {
+ loadMore: () => void;
+ loadMorePrev: () => void;
+ isReachingEnd?: boolean;
+ isReachingStart?: boolean;
+}
+
+export type HttpError =
+ | HttpResponseClientError
+ | HttpResponseServerError
+ | HttpResponseUnexpectedError;
+export interface SwrError {
+ info: unknown;
+ status: number;
+ message: string;
+}
+export interface HttpResponseServerError {
+ ok?: false;
+ loading?: false;
+ clientError?: false;
+ serverError: true;
+
+ error?: MerchantBackend.ErrorDetail;
+ status: number;
+ message: string;
+ info?: RequestInfo;
+}
+interface HttpResponseClientError {
+ ok?: false;
+ loading?: false;
+ clientError: true;
+ serverError?: false;
+
+ info?: RequestInfo;
+ isUnauthorized: boolean;
+ isNotfound: boolean;
+ status: number;
+ error?: MerchantBackend.ErrorDetail;
+ message: string;
+}
+
+interface HttpResponseUnexpectedError {
+ ok?: false;
+ loading?: false;
+ clientError?: false;
+ serverError?: false;
+
+ info?: RequestInfo;
+ status?: number;
+ error: unknown;
+ message: string;
+}
+
+type Methods = "get" | "post" | "patch" | "delete" | "put";
+
+interface RequestOptions {
+ method?: Methods;
+ token?: string;
+ data?: unknown;
+ params?: unknown;
+}
+
+function buildRequestOk<T>(
+ res: AxiosResponse<T>,
+ url: string,
+ hasToken: boolean
+): HttpResponseOk<T> {
+ return {
+ ok: true,
+ data: res.data,
+ info: {
+ params: res.config.params,
+ data: res.config.data,
+ url,
+ hasToken,
+ status: res.status,
+ },
+ };
+}
+
+// function buildResponse<T>(data?: T, error?: MerchantBackend.ErrorDetail, isValidating?: boolean): HttpResponse<T> {
+// if (isValidating) return {loading: true}
+// if (error) return buildRequestFailed()
+// }
+
+function buildRequestFailed(
+ ex: AxiosError<MerchantBackend.ErrorDetail>,
+ url: string,
+ hasToken: boolean
+):
+ | HttpResponseClientError
+ | HttpResponseServerError
+ | HttpResponseUnexpectedError {
+ const status = ex.response?.status;
+
+ const info: RequestInfo = {
+ data: ex.request?.data,
+ params: ex.request?.params,
+ url,
+ hasToken,
+ status: status || 0,
+ };
+
+ if (status && status >= 400 && status < 500) {
+ const error: HttpResponseClientError = {
+ clientError: true,
+ isNotfound: status === 404,
+ isUnauthorized: status === 401,
+ status,
+ info,
+ message: ex.response?.data?.hint || ex.message,
+ error: ex.response?.data,
+ };
+ return error;
+ }
+ if (status && status >= 500 && status < 600) {
+ const error: HttpResponseServerError = {
+ serverError: true,
+ status,
+ info,
+ message:
+ `${ex.response?.data?.hint} (code ${ex.response?.data?.code})` ||
+ ex.message,
+ error: ex.response?.data,
+ };
+ return error;
+ }
+
+ const error: HttpResponseUnexpectedError = {
+ info,
+ status,
+ error: ex,
+ message: ex.message,
+ };
+
+ return error;
+}
+
+const CancelToken = axios.CancelToken;
+let source = CancelToken.source();
+
+export function cancelPendingRequest(): void {
+ source.cancel("canceled by the user");
+ source = CancelToken.source();
+}
+
+export function isAxiosError<T>(
+ error: AxiosError | any
+): error is AxiosError<T> {
+ return error && error.isAxiosError;
+}
+
+export async function request<T>(
+ url: string,
+ options: RequestOptions = {}
+): Promise<HttpResponseOk<T>> {
+ const headers = options.token
+ ? { Authorization: `Bearer ${options.token}` }
+ : undefined;
+
+ try {
+ const res = await axiosHandler({
+ url,
+ responseType: "json",
+ headers,
+ cancelToken: !removeAxiosCancelToken ? source.token : undefined,
+ method: options.method || "get",
+ data: options.data,
+ params: options.params,
+ timeout: DEFAULT_REQUEST_TIMEOUT * 1000,
+ });
+ return buildRequestOk<T>(res, url, !!options.token);
+ } catch (e) {
+ if (isAxiosError<MerchantBackend.ErrorDetail>(e)) {
+ const error = buildRequestFailed(e, url, !!options.token);
+ throw error;
+ }
+ throw e;
+ }
+}
+
+export function multiFetcher<T>(
+ urls: string[],
+ token: string,
+ backend: string
+): Promise<HttpResponseOk<T>[]> {
+ return Promise.all(urls.map((url) => fetcher<T>(url, token, backend)));
+}
+
+export function fetcher<T>(
+ url: string,
+ token: string,
+ backend: string
+): Promise<HttpResponseOk<T>> {
+ return request<T>(`${backend}${url}`, { token });
+}
+
+export function useBackendInstancesTestForAdmin(): HttpResponse<MerchantBackend.Instances.InstancesResponse> {
+ const { url, token } = useBackendContext();
+
+ type Type = MerchantBackend.Instances.InstancesResponse;
+
+ const [result, setResult] = useState<HttpResponse<Type>>({ loading: true });
+
+ useEffect(() => {
+ request<Type>(`${url}/management/instances`, { token })
+ .then((data) => setResult(data))
+ .catch((error) => setResult(error));
+ }, [url, token]);
+
+ return result;
+}
+
+export function useBackendConfig(): HttpResponse<MerchantBackend.VersionResponse> {
+ const { url, token } = useBackendContext();
+
+ type Type = MerchantBackend.VersionResponse;
+
+ const [result, setResult] = useState<HttpResponse<Type>>({ loading: true });
+
+ useEffect(() => {
+ request<Type>(`${url}/config`, { token })
+ .then((data) => setResult(data))
+ .catch((error) => setResult(error));
+ }, [url, token]);
+
+ return result;
+}