/*
This file is part of GNU Taler
(C) 2021-2024 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
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import {
TalerErrorDetail,
TalerMerchantApi
} from "@gnu-taler/taler-util";
import {
EmptyObject,
HttpError,
HttpResponse,
HttpResponseOk,
RequestError,
RequestOptions,
useApiContext
} from "@gnu-taler/web-util/browser";
import { useCallback, useEffect, useState } from "preact/hooks";
import { useSWRConfig } from "swr";
import { useSessionContext } from "../context/session.js";
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) {
return mutate(
(key) => {
// evict if no key or regex === all
if (!key || !re) return true;
// match string
if (typeof key === "string" && re.test(key)) return true;
// record or object have the path at [0]
if (typeof key === "object" && re.test(key[0])) return true;
//key didn't match regex
return false;
},
undefined,
{
revalidate: true,
},
);
};
}
export function useBackendInstancesTestForAdmin(): HttpResponse<
TalerMerchantApi.InstancesResponse,
TalerErrorDetail
> {
const { request } = useBackendBaseRequest();
type Type = TalerMerchantApi.InstancesResponse;
const [result, setResult] = useState<
HttpResponse
>({ loading: true });
useEffect(() => {
request(`/management/instances`)
.then((data) => setResult(data))
.catch((error: RequestError) =>
setResult(error.cause),
);
}, [request]);
return result;
}
const CHECK_CONFIG_INTERVAL_OK = 5 * 60 * 1000;
const CHECK_CONFIG_INTERVAL_FAIL = 2 * 1000;
export function useBackendConfig(): HttpResponse<
TalerMerchantApi.VersionResponse | undefined,
RequestError
> {
const { request } = useBackendBaseRequest();
type Type = TalerMerchantApi.VersionResponse;
type State = {
data: HttpResponse>;
timer: number;
};
const [result, setResult] = useState({
data: { loading: true },
timer: 0,
});
useEffect(() => {
if (result.timer) {
clearTimeout(result.timer);
}
function tryConfig(): void {
request(`/config`)
.then((data) => {
const timer: any = setTimeout(() => {
tryConfig();
}, CHECK_CONFIG_INTERVAL_OK);
setResult({ data, timer });
})
.catch((error) => {
const timer: any = setTimeout(() => {
tryConfig();
}, CHECK_CONFIG_INTERVAL_FAIL);
const data = error.cause;
setResult({ data, timer });
});
}
tryConfig();
}, [request]);
return result.data;
}
interface useBackendInstanceRequestType {
request: (
endpoint: string,
options?: RequestOptions,
) => Promise>;
fetcher: (endpoint: string) => Promise>;
multiFetcher: (params: [url: string[]]) => Promise[]>;
orderFetcher: (
params: [
endpoint: string,
paid?: YesOrNo,
refunded?: YesOrNo,
wired?: YesOrNo,
searchDate?: Date,
delta?: number,
],
) => Promise>;
transferFetcher: (
params: [
endpoint: string,
payto_uri?: string,
verified?: string,
position?: string,
delta?: number,
],
) => Promise>;
templateFetcher: (
params: [endpoint: string, position?: string, delta?: number],
) => Promise>;
webhookFetcher: (
params: [endpoint: string, position?: string, delta?: number],
) => Promise>;
}
interface useBackendBaseRequestType {
request: (
endpoint: string,
options?: RequestOptions,
) => Promise>;
}
type YesOrNo = "yes" | "no";
type LoginResult =
| {
valid: true;
token: string;
expiration: Timestamp;
}
| {
valid: false;
cause: HttpError;
};
/**
*
* @param root the request is intended to the base URL and no the instance URL
* @returns request handler to
*/
export function useBackendBaseRequest(): useBackendBaseRequestType {
const { request: requestHandler } = useApiContext();
const { state } = useSessionContext();
const token = state.status === "loggedIn" ? state.token : undefined;
const baseUrl = state.backendUrl;
const request = useCallback(
function requestImpl(
endpoint: string,
options: RequestOptions = {},
): Promise> {
return requestHandler(baseUrl, endpoint, { ...options, token })
.then((res) => {
return res;
})
.catch((err) => {
throw err;
});
},
[baseUrl, token],
);
return { request };
}
export function useBackendInstanceRequest(): useBackendInstanceRequestType {
const { request: requestHandler } = useApiContext();
const { state } = useSessionContext();
const token = state.status === "loggedIn" ? state.token : undefined;
const baseUrl = state.backendUrl;
const request = useCallback(
function requestImpl(
endpoint: string,
options: RequestOptions = {},
): Promise> {
return requestHandler(baseUrl, endpoint, { token, ...options });
},
[baseUrl, token],
);
const multiFetcher = useCallback(
function multiFetcherImpl(
args: [endpoints: string[]],
): Promise[]> {
const [endpoints] = args;
return Promise.all(
endpoints.map((endpoint) =>
requestHandler(baseUrl, endpoint, { token }),
),
);
},
[baseUrl, token],
);
const fetcher = useCallback(
function fetcherImpl(endpoint: string): Promise> {
return requestHandler(baseUrl, endpoint, { token });
},
[baseUrl, token],
);
const orderFetcher = useCallback(
function orderFetcherImpl(
args: [
endpoint: string,
paid?: YesOrNo,
refunded?: YesOrNo,
wired?: YesOrNo,
searchDate?: Date,
delta?: number,
],
): Promise> {
const [endpoint, paid, refunded, wired, searchDate, delta] = args;
const date_s =
delta && delta < 0 && searchDate
? Math.floor(searchDate.getTime() / 1000) + 1
: searchDate !== undefined
? Math.floor(searchDate.getTime() / 1000)
: undefined;
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_s !== undefined) params.date_s = date_s;
if (delta === 0) {
//in this case we can already assume the response
//and avoid network
return Promise.resolve({
ok: true,
data: { orders: [] } as T,
});
}
return requestHandler(baseUrl, endpoint, { params, token });
},
[baseUrl, token],
);
const transferFetcher = useCallback(
function transferFetcherImpl(
args: [
endpoint: string,
payto_uri?: string,
verified?: string,
position?: string,
delta?: number,
],
): Promise> {
const [endpoint, payto_uri, verified, position, delta] = args;
const params: any = {};
if (payto_uri !== undefined) params.payto_uri = payto_uri;
if (verified !== undefined) params.verified = verified;
if (delta === 0) {
//in this case we can already assume the response
//and avoid network
return Promise.resolve({
ok: true,
data: { transfers: [] } as T,
});
}
if (delta !== undefined) {
params.limit = delta;
}
if (position !== undefined) params.offset = position;
return requestHandler(baseUrl, endpoint, { params, token });
},
[baseUrl, token],
);
const templateFetcher = useCallback(
function templateFetcherImpl(
args: [endpoint: string, position?: string, delta?: number],
): Promise> {
const [endpoint, position, delta] = args;
const params: any = {};
if (delta === 0) {
//in this case we can already assume the response
//and avoid network
return Promise.resolve({
ok: true,
data: { templates: [] } as T,
});
}
if (delta !== undefined) {
params.limit = delta;
}
if (position !== undefined) params.offset = position;
return requestHandler(baseUrl, endpoint, { params, token });
},
[baseUrl, token],
);
const webhookFetcher = useCallback(
function webhookFetcherImpl(
args: [endpoint: string, position?: string, delta?: number],
): Promise> {
const [endpoint, position, delta] = args;
const params: any = {};
if (delta === 0) {
//in this case we can already assume the response
//and avoid network
return Promise.resolve({
ok: true,
data: { webhooks: [] } as T,
});
}
if (delta !== undefined) {
params.limit = delta;
}
if (position !== undefined) params.offset = position;
return requestHandler(baseUrl, endpoint, { params, token });
},
[baseUrl, token],
);
return {
request,
fetcher,
multiFetcher,
orderFetcher,
transferFetcher,
templateFetcher,
webhookFetcher,
};
}