aboutsummaryrefslogtreecommitdiff
path: root/packages/merchant-backoffice-ui/src/hooks/backend.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/merchant-backoffice-ui/src/hooks/backend.ts')
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/backend.ts461
1 files changed, 213 insertions, 248 deletions
diff --git a/packages/merchant-backoffice-ui/src/hooks/backend.ts b/packages/merchant-backoffice-ui/src/hooks/backend.ts
index cbfac35de..a0639a4a0 100644
--- a/packages/merchant-backoffice-ui/src/hooks/backend.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/backend.ts
@@ -20,15 +20,16 @@
*/
import { useSWRConfig } from "swr";
-import axios, { AxiosError, AxiosResponse } from "axios";
import { MerchantBackend } from "../declaration.js";
import { useBackendContext } from "../context/backend.js";
-import { useEffect, useState } from "preact/hooks";
-import { DEFAULT_REQUEST_TIMEOUT } from "../utils/constants.js";
+import { useCallback, useEffect, useState } from "preact/hooks";
+import { useInstanceContext } from "../context/instance.js";
import {
- axiosHandler,
- removeAxiosCancelToken,
-} from "../utils/switchableAxios.js";
+ HttpResponse,
+ HttpResponseOk,
+ RequestOptions,
+} from "../utils/request.js";
+import { useApiContext } from "../context/api.js";
export function useMatchMutate(): (
re: RegExp,
@@ -44,9 +45,7 @@ export function useMatchMutate(): (
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);
@@ -55,268 +54,234 @@ export function useMatchMutate(): (
};
}
-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();
+ const { request } = useBackendBaseRequest();
type Type = MerchantBackend.Instances.InstancesResponse;
const [result, setResult] = useState<HttpResponse<Type>>({ loading: true });
useEffect(() => {
- request<Type>(`${url}/management/instances`, { token })
+ request<Type>(`/management/instances`)
.then((data) => setResult(data))
.catch((error) => setResult(error));
- }, [url, token]);
+ }, [request]);
return result;
}
export function useBackendConfig(): HttpResponse<MerchantBackend.VersionResponse> {
- const { url, token } = useBackendContext();
+ const { request } = useBackendBaseRequest();
type Type = MerchantBackend.VersionResponse;
const [result, setResult] = useState<HttpResponse<Type>>({ loading: true });
useEffect(() => {
- request<Type>(`${url}/config`, { token })
+ request<Type>(`/config`)
.then((data) => setResult(data))
.catch((error) => setResult(error));
- }, [url, token]);
+ }, [request]);
return result;
}
+
+interface useBackendInstanceRequestType {
+ request: <T>(
+ path: string,
+ options?: RequestOptions,
+ ) => Promise<HttpResponseOk<T>>;
+ fetcher: <T>(path: string) => Promise<HttpResponseOk<T>>;
+ reserveDetailFetcher: <T>(path: string) => Promise<HttpResponseOk<T>>;
+ tipsDetailFetcher: <T>(path: string) => Promise<HttpResponseOk<T>>;
+ multiFetcher: <T>(url: string[]) => Promise<HttpResponseOk<T>[]>;
+ orderFetcher: <T>(
+ path: string,
+ paid?: YesOrNo,
+ refunded?: YesOrNo,
+ wired?: YesOrNo,
+ searchDate?: Date,
+ delta?: number,
+ ) => Promise<HttpResponseOk<T>>;
+ transferFetcher: <T>(
+ path: string,
+ payto_uri?: string,
+ verified?: string,
+ position?: string,
+ delta?: number,
+ ) => Promise<HttpResponseOk<T>>;
+ templateFetcher: <T>(
+ path: string,
+ position?: string,
+ delta?: number,
+ ) => Promise<HttpResponseOk<T>>;
+}
+interface useBackendBaseRequestType {
+ request: <T>(
+ path: string,
+ options?: RequestOptions,
+ ) => Promise<HttpResponseOk<T>>;
+}
+
+type YesOrNo = "yes" | "no";
+
+/**
+ *
+ * @param root the request is intended to the base URL and no the instance URL
+ * @returns request handler to
+ */
+export function useBackendBaseRequest(): useBackendBaseRequestType {
+ const { url: backend, token } = useBackendContext();
+ const { request: requestHandler } = useApiContext();
+
+ const request = useCallback(
+ function requestImpl<T>(
+ path: string,
+ options: RequestOptions = {},
+ ): Promise<HttpResponseOk<T>> {
+ return requestHandler<T>(backend, path, { token, ...options });
+ },
+ [backend, token],
+ );
+
+ return { request };
+}
+
+export function useBackendInstanceRequest(): useBackendInstanceRequestType {
+ const { url: baseUrl, token: baseToken } = useBackendContext();
+ const { token: instanceToken, id, admin } = useInstanceContext();
+ const { request: requestHandler } = useApiContext();
+
+ const { backend, token } = !admin
+ ? { backend: baseUrl, token: baseToken }
+ : { backend: `${baseUrl}/instances/${id}`, token: instanceToken };
+
+ const request = useCallback(
+ function requestImpl<T>(
+ path: string,
+ options: RequestOptions = {},
+ ): Promise<HttpResponseOk<T>> {
+ return requestHandler<T>(backend, path, { token, ...options });
+ },
+ [backend, token],
+ );
+
+ const multiFetcher = useCallback(
+ function multiFetcherImpl<T>(
+ paths: string[],
+ ): Promise<HttpResponseOk<T>[]> {
+ return Promise.all(
+ paths.map((path) => requestHandler<T>(backend, path, { token })),
+ );
+ },
+ [backend, token],
+ );
+
+ const fetcher = useCallback(
+ function fetcherImpl<T>(path: string): Promise<HttpResponseOk<T>> {
+ return requestHandler<T>(backend, path, { token });
+ },
+ [backend, token],
+ );
+
+ const orderFetcher = useCallback(
+ function orderFetcherImpl<T>(
+ path: string,
+ paid?: YesOrNo,
+ refunded?: YesOrNo,
+ wired?: YesOrNo,
+ searchDate?: Date,
+ delta?: number,
+ ): Promise<HttpResponseOk<T>> {
+ const date_ms =
+ delta && delta < 0 && searchDate
+ ? searchDate.getTime() + 1
+ : searchDate?.getTime();
+ const params: any = {};
+ if (paid !== undefined) params.paid = paid;
+ if (delta !== undefined) params.delta = delta;
+ if (refunded !== undefined) params.refunded = refunded;
+ if (wired !== undefined) params.wired = wired;
+ if (date_ms !== undefined) params.date_ms = date_ms;
+ return requestHandler<T>(backend, path, { params, token });
+ },
+ [backend, token],
+ );
+
+ const reserveDetailFetcher = useCallback(
+ function reserveDetailFetcherImpl<T>(
+ path: string,
+ ): Promise<HttpResponseOk<T>> {
+ return requestHandler<T>(backend, path, {
+ params: {
+ tips: "yes",
+ },
+ token,
+ });
+ },
+ [backend, token],
+ );
+
+ const tipsDetailFetcher = useCallback(
+ function tipsDetailFetcherImpl<T>(
+ path: string,
+ ): Promise<HttpResponseOk<T>> {
+ return requestHandler<T>(backend, path, {
+ params: {
+ pickups: "yes",
+ },
+ token,
+ });
+ },
+ [backend, token],
+ );
+
+ const transferFetcher = useCallback(
+ function transferFetcherImpl<T>(
+ path: string,
+ payto_uri?: string,
+ verified?: string,
+ position?: string,
+ delta?: number,
+ ): Promise<HttpResponseOk<T>> {
+ const params: any = {};
+ if (payto_uri !== undefined) params.payto_uri = payto_uri;
+ if (verified !== undefined) params.verified = verified;
+ if (delta !== undefined) {
+ params.limit = delta;
+ }
+ if (position !== undefined) params.offset = position;
+
+ return requestHandler<T>(backend, path, { params, token });
+ },
+ [backend, token],
+ );
+
+ const templateFetcher = useCallback(
+ function templateFetcherImpl<T>(
+ path: string,
+ position?: string,
+ delta?: number,
+ ): Promise<HttpResponseOk<T>> {
+ const params: any = {};
+ if (delta !== undefined) {
+ params.limit = delta;
+ }
+ if (position !== undefined) params.offset = position;
+
+ return requestHandler<T>(backend, path, { params, token });
+ },
+ [backend, token],
+ );
+
+ return {
+ request,
+ fetcher,
+ multiFetcher,
+ orderFetcher,
+ reserveDetailFetcher,
+ tipsDetailFetcher,
+ transferFetcher,
+ templateFetcher,
+ };
+}