/* 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) * @author Nic Eigel */ import { AbsoluteTime, HttpStatusCode } from "@gnu-taler/taler-util"; import { ErrorType, HttpError, HttpResponse, HttpResponseOk, RequestError, RequestOptions, useApiContext, } from "@gnu-taler/web-util/browser"; import { useCallback, useEffect, useState } from "preact/hooks"; import { useSWRConfig } from "swr"; import { useBackendContext } from "../context/backend.js"; import { useInstanceContext } from "../context/instance.js"; import { AuditorBackend, Timestamp } from "../declaration.js"; /* export function tryConfig(): Promise { // const request: RequestInfo = new Request('./Config.json', { // method: 'GET', // headers: headers // }) return fetch("/config.json") // the JSON body is taken from the response .then(res => res.json()) .then(res => { // The response has an `any` type, so we need to cast // it to the `User` type, and return it from the promise return res as Config; }); }*/ 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, }); }; } type YesOrNo = "yes" | "no"; interface useBackendInstanceRequestType { request: ( endpoint: string, options?: RequestOptions, ) => Promise>; depositConfirmationFetcher: ( params: [endpoint: string, paid?: YesOrNo, refunded?: YesOrNo, wired?: YesOrNo, searchDate?: Date, delta?: number,] ) => Promise>; } interface useBackendBaseRequestType { request: ( endpoint: string, options?: RequestOptions, ) => Promise>; } /** * * @param root the request is intended to the base URL and no the instance URL * @returns request handler to */ //TODO: Add token export function useBackendBaseRequest(): useBackendBaseRequestType { const { url: backend} = useBackendContext(); const { request: requestHandler } = useApiContext(); const request = useCallback( function requestImpl( endpoint: string, options: RequestOptions = {}, ): Promise> { return requestHandler(backend, endpoint, { ...options }).then(res => { return res }).catch(err => { throw err }); }, [backend], ); return { request }; } const CHECK_CONFIG_INTERVAL_OK = 5 * 60 * 1000; const CHECK_CONFIG_INTERVAL_FAIL = 2 * 1000; export function useBackendConfig(): HttpResponse< AuditorBackend.VersionResponse | undefined, RequestError > { const { request } = useBackendBaseRequest(); type Type = AuditorBackend.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 { //TODO change back to /config request(`http://localhost:8083/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; } export function useBackendInstanceRequest(): useBackendInstanceRequestType { const { url: rootBackendUrl} = useBackendContext(); const { id } = useInstanceContext(); const { request: requestHandler } = useApiContext(); const baseUrl = rootBackendUrl; const request = useCallback( function requestImpl( endpoint: string, options: RequestOptions = {}, ): Promise> { return requestHandler(baseUrl, endpoint, { ...options }); }, [baseUrl], ); const multiFetcher = useCallback( function multiFetcherImpl( args: [endpoints: string[]], ): Promise[]> { const [endpoints] = args return Promise.all( endpoints.map((endpoint) => requestHandler(baseUrl, endpoint, ), ), ); }, [baseUrl], ); const fetcher = useCallback( function fetcherImpl(endpoint: string): Promise> { return requestHandler(baseUrl, endpoint ); }, [baseUrl], ); const depositConfirmationFetcher = 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 }); }, [baseUrl], ); return { request, depositConfirmationFetcher, };}