use request api from web-util

This commit is contained in:
Sebastian 2023-02-08 17:39:39 -03:00
parent be01d1479c
commit 603efbd073
No known key found for this signature in database
GPG Key ID: BE4FF68352439FC1
33 changed files with 320 additions and 511 deletions

View File

@ -19,7 +19,10 @@
* @author Sebastian Javier Marchano (sebasjm) * @author Sebastian Javier Marchano (sebasjm)
*/ */
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; import {
useTranslationContext,
HttpError,
} from "@gnu-taler/web-util/lib/index.browser";
import { format } from "date-fns"; import { format } from "date-fns";
import { Fragment, FunctionComponent, h, VNode } from "preact"; import { Fragment, FunctionComponent, h, VNode } from "preact";
import { Route, route, Router } from "preact-router"; import { Route, route, Router } from "preact-router";
@ -28,7 +31,6 @@ import { Loading } from "./components/exception/loading.js";
import { Menu, NotificationCard } from "./components/menu/index.js"; import { Menu, NotificationCard } from "./components/menu/index.js";
import { useBackendContext } from "./context/backend.js"; import { useBackendContext } from "./context/backend.js";
import { InstanceContextProvider } from "./context/instance.js"; import { InstanceContextProvider } from "./context/instance.js";
import { HttpError } from "./utils/request.js";
import { import {
useBackendDefaultToken, useBackendDefaultToken,
useBackendInstanceToken, useBackendInstanceToken,
@ -63,6 +65,7 @@ import InstanceUpdatePage, {
import LoginPage from "./paths/login/index.js"; import LoginPage from "./paths/login/index.js";
import NotFoundPage from "./paths/notfound/index.js"; import NotFoundPage from "./paths/notfound/index.js";
import { Notification } from "./utils/types.js"; import { Notification } from "./utils/types.js";
import { MerchantBackend } from "./declaration.js";
export enum InstancePaths { export enum InstancePaths {
// details = '/', // details = '/',
@ -157,7 +160,9 @@ export function InstanceRoutes({
); );
function ServerErrorRedirectTo(to: InstancePaths | AdminPaths) { function ServerErrorRedirectTo(to: InstancePaths | AdminPaths) {
return function ServerErrorRedirectToImpl(error: HttpError) { return function ServerErrorRedirectToImpl(
error: HttpError<MerchantBackend.ErrorDetail>,
) {
setGlobalNotification({ setGlobalNotification({
message: i18n.str`The backend reported a problem: HTTP status #${error.status}`, message: i18n.str`The backend reported a problem: HTTP status #${error.status}`,
description: i18n.str`Diagnostic from ${error.info?.url} is "${error.message}"`, description: i18n.str`Diagnostic from ${error.info?.url} is "${error.message}"`,
@ -551,7 +556,7 @@ function AdminInstanceUpdatePage({
}: { id: string } & InstanceUpdatePageProps): VNode { }: { id: string } & InstanceUpdatePageProps): VNode {
const [token, changeToken] = useBackendInstanceToken(id); const [token, changeToken] = useBackendInstanceToken(id);
const { updateLoginStatus: changeBackend } = useBackendContext(); const { updateLoginStatus: changeBackend } = useBackendContext();
const updateLoginStatus = (url: string, token?: string) => { const updateLoginStatus = (url: string, token?: string): void => {
changeBackend(url); changeBackend(url);
if (token) changeToken(token); if (token) changeToken(token);
}; };
@ -566,7 +571,7 @@ function AdminInstanceUpdatePage({
<InstanceAdminUpdatePage <InstanceAdminUpdatePage
{...rest} {...rest}
instanceId={id} instanceId={id}
onLoadError={(error: HttpError) => { onLoadError={(error: HttpError<MerchantBackend.ErrorDetail>) => {
return ( return (
<Fragment> <Fragment>
<NotificationCard <NotificationCard

View File

@ -86,7 +86,7 @@ export function InputImage<T>({
} }
setSizeError(false); setSizeError(false);
return f[0].arrayBuffer().then((b) => { return f[0].arrayBuffer().then((b) => {
const b64 = btoa( const b64 = window.btoa(
new Uint8Array(b).reduce( new Uint8Array(b).reduce(
(data, byte) => data + String.fromCharCode(byte), (data, byte) => data + String.fromCharCode(byte),
"", "",

View File

@ -1,43 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021-2023 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 { ComponentChildren, createContext, h, VNode } from "preact";
import { useContext } from "preact/hooks";
import { defaultRequestHandler } from "../utils/request.js";
interface Type {
request: typeof defaultRequestHandler;
}
const Context = createContext<Type>({
request: defaultRequestHandler,
});
export const useApiContext = (): Type => useContext(Context);
export const ApiContextProvider = ({
children,
value,
}: {
value: Type;
children: ComponentChildren;
}): VNode => {
return h(Context.Provider, { value, children });
};

View File

@ -1355,14 +1355,13 @@ export namespace MerchantBackend {
interface UsingTemplateResponse { interface UsingTemplateResponse {
// After enter the request. The user will be pay with a taler URL. // After enter the request. The user will be pay with a taler URL.
order_id: string, order_id: string;
token: string, token: string;
} }
} }
namespace Webhooks { namespace Webhooks {
interface WebhookAddDetails { interface WebhookAddDetails {
// Webhook ID to use. // Webhook ID to use.
webhook_id: string; webhook_id: string;
@ -1380,10 +1379,8 @@ export namespace MerchantBackend {
// Body template by the webhook // Body template by the webhook
body_template?: string; body_template?: string;
} }
interface WebhookPatchDetails { interface WebhookPatchDetails {
// The event of the webhook: why the webhook is used. // The event of the webhook: why the webhook is used.
event_type: string; event_type: string;
@ -1398,25 +1395,19 @@ export namespace MerchantBackend {
// Body template by the webhook // Body template by the webhook
body_template?: string; body_template?: string;
} }
interface WebhookSummaryResponse { interface WebhookSummaryResponse {
// List of webhooks that are present in our backend. // List of webhooks that are present in our backend.
webhooks: WebhookEntry[]; webhooks: WebhookEntry[];
} }
interface WebhookEntry { interface WebhookEntry {
// Webhook identifier, as found in the webhook. // Webhook identifier, as found in the webhook.
webhook_id: string; webhook_id: string;
// The event of the webhook: why the webhook is used. // The event of the webhook: why the webhook is used.
event_type: string; event_type: string;
} }
interface WebhookDetails { interface WebhookDetails {
// The event of the webhook: why the webhook is used. // The event of the webhook: why the webhook is used.
event_type: string; event_type: string;
@ -1431,9 +1422,7 @@ export namespace MerchantBackend {
// Body template by the webhook // Body template by the webhook
body_template?: string; body_template?: string;
} }
} }
interface ContractTerms { interface ContractTerms {

View File

@ -28,8 +28,8 @@ import {
HttpResponse, HttpResponse,
HttpResponseOk, HttpResponseOk,
RequestOptions, RequestOptions,
} from "../utils/request.js"; } from "@gnu-taler/web-util/lib/index.browser";
import { useApiContext } from "../context/api.js"; import { useApiContext } from "@gnu-taler/web-util/lib/index.browser";
export function useMatchMutate(): ( export function useMatchMutate(): (
re: RegExp, re: RegExp,
@ -54,12 +54,17 @@ export function useMatchMutate(): (
}; };
} }
export function useBackendInstancesTestForAdmin(): HttpResponse<MerchantBackend.Instances.InstancesResponse> { export function useBackendInstancesTestForAdmin(): HttpResponse<
MerchantBackend.Instances.InstancesResponse,
MerchantBackend.ErrorDetail
> {
const { request } = useBackendBaseRequest(); const { request } = useBackendBaseRequest();
type Type = MerchantBackend.Instances.InstancesResponse; type Type = MerchantBackend.Instances.InstancesResponse;
const [result, setResult] = useState<HttpResponse<Type>>({ loading: true }); const [result, setResult] = useState<
HttpResponse<Type, MerchantBackend.ErrorDetail>
>({ loading: true });
useEffect(() => { useEffect(() => {
request<Type>(`/management/instances`) request<Type>(`/management/instances`)
@ -70,12 +75,17 @@ export function useBackendInstancesTestForAdmin(): HttpResponse<MerchantBackend.
return result; return result;
} }
export function useBackendConfig(): HttpResponse<MerchantBackend.VersionResponse> { export function useBackendConfig(): HttpResponse<
MerchantBackend.VersionResponse,
MerchantBackend.ErrorDetail
> {
const { request } = useBackendBaseRequest(); const { request } = useBackendBaseRequest();
type Type = MerchantBackend.VersionResponse; type Type = MerchantBackend.VersionResponse;
const [result, setResult] = useState<HttpResponse<Type>>({ loading: true }); const [result, setResult] = useState<
HttpResponse<Type, MerchantBackend.ErrorDetail>
>({ loading: true });
useEffect(() => { useEffect(() => {
request<Type>(`/config`) request<Type>(`/config`)
@ -88,15 +98,15 @@ export function useBackendConfig(): HttpResponse<MerchantBackend.VersionResponse
interface useBackendInstanceRequestType { interface useBackendInstanceRequestType {
request: <T>( request: <T>(
path: string, endpoint: string,
options?: RequestOptions, options?: RequestOptions,
) => Promise<HttpResponseOk<T>>; ) => Promise<HttpResponseOk<T>>;
fetcher: <T>(path: string) => Promise<HttpResponseOk<T>>; fetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
reserveDetailFetcher: <T>(path: string) => Promise<HttpResponseOk<T>>; reserveDetailFetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
tipsDetailFetcher: <T>(path: string) => Promise<HttpResponseOk<T>>; tipsDetailFetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
multiFetcher: <T>(url: string[]) => Promise<HttpResponseOk<T>[]>; multiFetcher: <T>(url: string[]) => Promise<HttpResponseOk<T>[]>;
orderFetcher: <T>( orderFetcher: <T>(
path: string, endpoint: string,
paid?: YesOrNo, paid?: YesOrNo,
refunded?: YesOrNo, refunded?: YesOrNo,
wired?: YesOrNo, wired?: YesOrNo,
@ -104,26 +114,26 @@ interface useBackendInstanceRequestType {
delta?: number, delta?: number,
) => Promise<HttpResponseOk<T>>; ) => Promise<HttpResponseOk<T>>;
transferFetcher: <T>( transferFetcher: <T>(
path: string, endpoint: string,
payto_uri?: string, payto_uri?: string,
verified?: string, verified?: string,
position?: string, position?: string,
delta?: number, delta?: number,
) => Promise<HttpResponseOk<T>>; ) => Promise<HttpResponseOk<T>>;
templateFetcher: <T>( templateFetcher: <T>(
path: string, endpoint: string,
position?: string, position?: string,
delta?: number, delta?: number,
) => Promise<HttpResponseOk<T>>; ) => Promise<HttpResponseOk<T>>;
webhookFetcher: <T>( webhookFetcher: <T>(
path: string, endpoint: string,
position?: string, position?: string,
delta?: number, delta?: number,
) => Promise<HttpResponseOk<T>>; ) => Promise<HttpResponseOk<T>>;
} }
interface useBackendBaseRequestType { interface useBackendBaseRequestType {
request: <T>( request: <T>(
path: string, endpoint: string,
options?: RequestOptions, options?: RequestOptions,
) => Promise<HttpResponseOk<T>>; ) => Promise<HttpResponseOk<T>>;
} }
@ -141,10 +151,10 @@ export function useBackendBaseRequest(): useBackendBaseRequestType {
const request = useCallback( const request = useCallback(
function requestImpl<T>( function requestImpl<T>(
path: string, endpoint: string,
options: RequestOptions = {}, options: RequestOptions = {},
): Promise<HttpResponseOk<T>> { ): Promise<HttpResponseOk<T>> {
return requestHandler<T>(backend, path, { token, ...options }); return requestHandler<T>(backend, endpoint, { token, ...options });
}, },
[backend, token], [backend, token],
); );
@ -153,45 +163,47 @@ export function useBackendBaseRequest(): useBackendBaseRequestType {
} }
export function useBackendInstanceRequest(): useBackendInstanceRequestType { export function useBackendInstanceRequest(): useBackendInstanceRequestType {
const { url: baseUrl, token: baseToken } = useBackendContext(); const { url: rootBackendUrl, token: rootToken } = useBackendContext();
const { token: instanceToken, id, admin } = useInstanceContext(); const { token: instanceToken, id, admin } = useInstanceContext();
const { request: requestHandler } = useApiContext(); const { request: requestHandler } = useApiContext();
const { backend, token } = !admin const { baseUrl, token } = !admin
? { backend: baseUrl, token: baseToken } ? { baseUrl: rootBackendUrl, token: rootToken }
: { backend: `${baseUrl}/instances/${id}`, token: instanceToken }; : { baseUrl: `${rootBackendUrl}/instances/${id}`, token: instanceToken };
const request = useCallback( const request = useCallback(
function requestImpl<T>( function requestImpl<T>(
path: string, endpoint: string,
options: RequestOptions = {}, options: RequestOptions = {},
): Promise<HttpResponseOk<T>> { ): Promise<HttpResponseOk<T>> {
return requestHandler<T>(backend, path, { token, ...options }); return requestHandler<T>(baseUrl, endpoint, { token, ...options });
}, },
[backend, token], [baseUrl, token],
); );
const multiFetcher = useCallback( const multiFetcher = useCallback(
function multiFetcherImpl<T>( function multiFetcherImpl<T>(
paths: string[], endpoints: string[],
): Promise<HttpResponseOk<T>[]> { ): Promise<HttpResponseOk<T>[]> {
return Promise.all( return Promise.all(
paths.map((path) => requestHandler<T>(backend, path, { token })), endpoints.map((endpoint) =>
requestHandler<T>(baseUrl, endpoint, { token }),
),
); );
}, },
[backend, token], [baseUrl, token],
); );
const fetcher = useCallback( const fetcher = useCallback(
function fetcherImpl<T>(path: string): Promise<HttpResponseOk<T>> { function fetcherImpl<T>(endpoint: string): Promise<HttpResponseOk<T>> {
return requestHandler<T>(backend, path, { token }); return requestHandler<T>(baseUrl, endpoint, { token });
}, },
[backend, token], [baseUrl, token],
); );
const orderFetcher = useCallback( const orderFetcher = useCallback(
function orderFetcherImpl<T>( function orderFetcherImpl<T>(
path: string, endpoint: string,
paid?: YesOrNo, paid?: YesOrNo,
refunded?: YesOrNo, refunded?: YesOrNo,
wired?: YesOrNo, wired?: YesOrNo,
@ -208,42 +220,42 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
if (refunded !== undefined) params.refunded = refunded; if (refunded !== undefined) params.refunded = refunded;
if (wired !== undefined) params.wired = wired; if (wired !== undefined) params.wired = wired;
if (date_ms !== undefined) params.date_ms = date_ms; if (date_ms !== undefined) params.date_ms = date_ms;
return requestHandler<T>(backend, path, { params, token }); return requestHandler<T>(baseUrl, endpoint, { params, token });
}, },
[backend, token], [baseUrl, token],
); );
const reserveDetailFetcher = useCallback( const reserveDetailFetcher = useCallback(
function reserveDetailFetcherImpl<T>( function reserveDetailFetcherImpl<T>(
path: string, endpoint: string,
): Promise<HttpResponseOk<T>> { ): Promise<HttpResponseOk<T>> {
return requestHandler<T>(backend, path, { return requestHandler<T>(baseUrl, endpoint, {
params: { params: {
tips: "yes", tips: "yes",
}, },
token, token,
}); });
}, },
[backend, token], [baseUrl, token],
); );
const tipsDetailFetcher = useCallback( const tipsDetailFetcher = useCallback(
function tipsDetailFetcherImpl<T>( function tipsDetailFetcherImpl<T>(
path: string, endpoint: string,
): Promise<HttpResponseOk<T>> { ): Promise<HttpResponseOk<T>> {
return requestHandler<T>(backend, path, { return requestHandler<T>(baseUrl, endpoint, {
params: { params: {
pickups: "yes", pickups: "yes",
}, },
token, token,
}); });
}, },
[backend, token], [baseUrl, token],
); );
const transferFetcher = useCallback( const transferFetcher = useCallback(
function transferFetcherImpl<T>( function transferFetcherImpl<T>(
path: string, endpoint: string,
payto_uri?: string, payto_uri?: string,
verified?: string, verified?: string,
position?: string, position?: string,
@ -257,14 +269,14 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
} }
if (position !== undefined) params.offset = position; if (position !== undefined) params.offset = position;
return requestHandler<T>(backend, path, { params, token }); return requestHandler<T>(baseUrl, endpoint, { params, token });
}, },
[backend, token], [baseUrl, token],
); );
const templateFetcher = useCallback( const templateFetcher = useCallback(
function templateFetcherImpl<T>( function templateFetcherImpl<T>(
path: string, endpoint: string,
position?: string, position?: string,
delta?: number, delta?: number,
): Promise<HttpResponseOk<T>> { ): Promise<HttpResponseOk<T>> {
@ -274,14 +286,14 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
} }
if (position !== undefined) params.offset = position; if (position !== undefined) params.offset = position;
return requestHandler<T>(backend, path, { params, token }); return requestHandler<T>(baseUrl, endpoint, { params, token });
}, },
[backend, token], [baseUrl, token],
); );
const webhookFetcher = useCallback( const webhookFetcher = useCallback(
function webhookFetcherImpl<T>( function webhookFetcherImpl<T>(
path: string, endpoint: string,
position?: string, position?: string,
delta?: number, delta?: number,
): Promise<HttpResponseOk<T>> { ): Promise<HttpResponseOk<T>> {
@ -291,9 +303,9 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
} }
if (position !== undefined) params.offset = position; if (position !== undefined) params.offset = position;
return requestHandler<T>(backend, path, { params, token }); return requestHandler<T>(baseUrl, endpoint, { params, token });
}, },
[backend, token], [baseUrl, token],
); );
return { return {

View File

@ -16,7 +16,11 @@
import useSWR, { useSWRConfig } from "swr"; import useSWR, { useSWRConfig } from "swr";
import { useBackendContext } from "../context/backend.js"; import { useBackendContext } from "../context/backend.js";
import { MerchantBackend } from "../declaration.js"; import { MerchantBackend } from "../declaration.js";
import { HttpError, HttpResponse, HttpResponseOk } from "../utils/request.js"; import {
HttpError,
HttpResponse,
HttpResponseOk,
} from "@gnu-taler/web-util/lib/index.browser";
import { import {
useBackendBaseRequest, useBackendBaseRequest,
useBackendInstanceRequest, useBackendInstanceRequest,
@ -176,12 +180,15 @@ export function useInstanceAPI(): InstanceAPI {
return { updateInstance, deleteInstance, setNewToken, clearToken }; return { updateInstance, deleteInstance, setNewToken, clearToken };
} }
export function useInstanceDetails(): HttpResponse<MerchantBackend.Instances.QueryInstancesResponse> { export function useInstanceDetails(): HttpResponse<
MerchantBackend.Instances.QueryInstancesResponse,
MerchantBackend.ErrorDetail
> {
const { fetcher } = useBackendInstanceRequest(); const { fetcher } = useBackendInstanceRequest();
const { data, error, isValidating } = useSWR< const { data, error, isValidating } = useSWR<
HttpResponseOk<MerchantBackend.Instances.QueryInstancesResponse>, HttpResponseOk<MerchantBackend.Instances.QueryInstancesResponse>,
HttpError HttpError<MerchantBackend.ErrorDetail>
>([`/private/`], fetcher, { >([`/private/`], fetcher, {
refreshInterval: 0, refreshInterval: 0,
refreshWhenHidden: false, refreshWhenHidden: false,
@ -203,12 +210,15 @@ type KYCStatus =
| { type: "ok" } | { type: "ok" }
| { type: "redirect"; status: MerchantBackend.Instances.AccountKycRedirects }; | { type: "redirect"; status: MerchantBackend.Instances.AccountKycRedirects };
export function useInstanceKYCDetails(): HttpResponse<KYCStatus> { export function useInstanceKYCDetails(): HttpResponse<
KYCStatus,
MerchantBackend.ErrorDetail
> {
const { fetcher } = useBackendInstanceRequest(); const { fetcher } = useBackendInstanceRequest();
const { data, error } = useSWR< const { data, error } = useSWR<
HttpResponseOk<MerchantBackend.Instances.AccountKycRedirects>, HttpResponseOk<MerchantBackend.Instances.AccountKycRedirects>,
HttpError HttpError<MerchantBackend.ErrorDetail>
>([`/private/kyc`], fetcher, { >([`/private/kyc`], fetcher, {
refreshInterval: 5000, refreshInterval: 5000,
refreshWhenHidden: false, refreshWhenHidden: false,
@ -231,12 +241,15 @@ export function useInstanceKYCDetails(): HttpResponse<KYCStatus> {
export function useManagedInstanceDetails( export function useManagedInstanceDetails(
instanceId: string, instanceId: string,
): HttpResponse<MerchantBackend.Instances.QueryInstancesResponse> { ): HttpResponse<
MerchantBackend.Instances.QueryInstancesResponse,
MerchantBackend.ErrorDetail
> {
const { request } = useBackendBaseRequest(); const { request } = useBackendBaseRequest();
const { data, error, isValidating } = useSWR< const { data, error, isValidating } = useSWR<
HttpResponseOk<MerchantBackend.Instances.QueryInstancesResponse>, HttpResponseOk<MerchantBackend.Instances.QueryInstancesResponse>,
HttpError HttpError<MerchantBackend.ErrorDetail>
>([`/management/instances/${instanceId}`], request, { >([`/management/instances/${instanceId}`], request, {
refreshInterval: 0, refreshInterval: 0,
refreshWhenHidden: false, refreshWhenHidden: false,
@ -254,12 +267,15 @@ export function useManagedInstanceDetails(
return { loading: true }; return { loading: true };
} }
export function useBackendInstances(): HttpResponse<MerchantBackend.Instances.InstancesResponse> { export function useBackendInstances(): HttpResponse<
MerchantBackend.Instances.InstancesResponse,
MerchantBackend.ErrorDetail
> {
const { request } = useBackendBaseRequest(); const { request } = useBackendBaseRequest();
const { data, error, isValidating } = useSWR< const { data, error, isValidating } = useSWR<
HttpResponseOk<MerchantBackend.Instances.InstancesResponse>, HttpResponseOk<MerchantBackend.Instances.InstancesResponse>,
HttpError HttpError<MerchantBackend.ErrorDetail>
>(["/management/instances"], request); >(["/management/instances"], request);
if (isValidating) return { loading: true, data: data?.data }; if (isValidating) return { loading: true, data: data?.data };

View File

@ -22,7 +22,7 @@ import {
HttpResponse, HttpResponse,
HttpResponseOk, HttpResponseOk,
HttpResponsePaginated, HttpResponsePaginated,
} from "../utils/request.js"; } from "@gnu-taler/web-util/lib/index.browser";
import { useBackendInstanceRequest, useMatchMutate } from "./backend.js"; import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
export interface OrderAPI { export interface OrderAPI {
@ -128,12 +128,15 @@ export function useOrderAPI(): OrderAPI {
export function useOrderDetails( export function useOrderDetails(
oderId: string, oderId: string,
): HttpResponse<MerchantBackend.Orders.MerchantOrderStatusResponse> { ): HttpResponse<
MerchantBackend.Orders.MerchantOrderStatusResponse,
MerchantBackend.ErrorDetail
> {
const { fetcher } = useBackendInstanceRequest(); const { fetcher } = useBackendInstanceRequest();
const { data, error, isValidating } = useSWR< const { data, error, isValidating } = useSWR<
HttpResponseOk<MerchantBackend.Orders.MerchantOrderStatusResponse>, HttpResponseOk<MerchantBackend.Orders.MerchantOrderStatusResponse>,
HttpError HttpError<MerchantBackend.ErrorDetail>
>([`/private/orders/${oderId}`], fetcher, { >([`/private/orders/${oderId}`], fetcher, {
refreshInterval: 0, refreshInterval: 0,
refreshWhenHidden: false, refreshWhenHidden: false,
@ -158,7 +161,10 @@ export interface InstanceOrderFilter {
export function useInstanceOrders( export function useInstanceOrders(
args?: InstanceOrderFilter, args?: InstanceOrderFilter,
updateFilter?: (d: Date) => void, updateFilter?: (d: Date) => void,
): HttpResponsePaginated<MerchantBackend.Orders.OrderHistory> { ): HttpResponsePaginated<
MerchantBackend.Orders.OrderHistory,
MerchantBackend.ErrorDetail
> {
const { orderFetcher } = useBackendInstanceRequest(); const { orderFetcher } = useBackendInstanceRequest();
const [pageBefore, setPageBefore] = useState(1); const [pageBefore, setPageBefore] = useState(1);
@ -177,7 +183,10 @@ export function useInstanceOrders(
data: beforeData, data: beforeData,
error: beforeError, error: beforeError,
isValidating: loadingBefore, isValidating: loadingBefore,
} = useSWR<HttpResponseOk<MerchantBackend.Orders.OrderHistory>, HttpError>( } = useSWR<
HttpResponseOk<MerchantBackend.Orders.OrderHistory>,
HttpError<MerchantBackend.ErrorDetail>
>(
[ [
`/private/orders`, `/private/orders`,
args?.paid, args?.paid,
@ -192,7 +201,10 @@ export function useInstanceOrders(
data: afterData, data: afterData,
error: afterError, error: afterError,
isValidating: loadingAfter, isValidating: loadingAfter,
} = useSWR<HttpResponseOk<MerchantBackend.Orders.OrderHistory>, HttpError>( } = useSWR<
HttpResponseOk<MerchantBackend.Orders.OrderHistory>,
HttpError<MerchantBackend.ErrorDetail>
>(
[ [
`/private/orders`, `/private/orders`,
args?.paid, args?.paid,
@ -206,10 +218,16 @@ export function useInstanceOrders(
//this will save last result //this will save last result
const [lastBefore, setLastBefore] = useState< const [lastBefore, setLastBefore] = useState<
HttpResponse<MerchantBackend.Orders.OrderHistory> HttpResponse<
MerchantBackend.Orders.OrderHistory,
MerchantBackend.ErrorDetail
>
>({ loading: true }); >({ loading: true });
const [lastAfter, setLastAfter] = useState< const [lastAfter, setLastAfter] = useState<
HttpResponse<MerchantBackend.Orders.OrderHistory> HttpResponse<
MerchantBackend.Orders.OrderHistory,
MerchantBackend.ErrorDetail
>
>({ loading: true }); >({ loading: true });
useEffect(() => { useEffect(() => {
if (afterData) setLastAfter(afterData); if (afterData) setLastAfter(afterData);

View File

@ -15,7 +15,11 @@
*/ */
import useSWR, { useSWRConfig } from "swr"; import useSWR, { useSWRConfig } from "swr";
import { MerchantBackend, WithId } from "../declaration.js"; import { MerchantBackend, WithId } from "../declaration.js";
import { HttpError, HttpResponse, HttpResponseOk } from "../utils/request.js"; import {
HttpError,
HttpResponse,
HttpResponseOk,
} from "@gnu-taler/web-util/lib/index.browser";
import { useBackendInstanceRequest, useMatchMutate } from "./backend.js"; import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
export interface ProductAPI { export interface ProductAPI {
@ -85,13 +89,14 @@ export function useProductAPI(): ProductAPI {
} }
export function useInstanceProducts(): HttpResponse< export function useInstanceProducts(): HttpResponse<
(MerchantBackend.Products.ProductDetail & WithId)[] (MerchantBackend.Products.ProductDetail & WithId)[],
MerchantBackend.ErrorDetail
> { > {
const { fetcher, multiFetcher } = useBackendInstanceRequest(); const { fetcher, multiFetcher } = useBackendInstanceRequest();
const { data: list, error: listError } = useSWR< const { data: list, error: listError } = useSWR<
HttpResponseOk<MerchantBackend.Products.InventorySummaryResponse>, HttpResponseOk<MerchantBackend.Products.InventorySummaryResponse>,
HttpError HttpError<MerchantBackend.ErrorDetail>
>([`/private/products`], fetcher, { >([`/private/products`], fetcher, {
refreshInterval: 0, refreshInterval: 0,
refreshWhenHidden: false, refreshWhenHidden: false,
@ -105,7 +110,7 @@ export function useInstanceProducts(): HttpResponse<
); );
const { data: products, error: productError } = useSWR< const { data: products, error: productError } = useSWR<
HttpResponseOk<MerchantBackend.Products.ProductDetail>[], HttpResponseOk<MerchantBackend.Products.ProductDetail>[],
HttpError HttpError<MerchantBackend.ErrorDetail>
>([paths], multiFetcher, { >([paths], multiFetcher, {
refreshInterval: 0, refreshInterval: 0,
refreshWhenHidden: false, refreshWhenHidden: false,
@ -122,7 +127,7 @@ export function useInstanceProducts(): HttpResponse<
//take the id from the queried url //take the id from the queried url
return { return {
...d.data, ...d.data,
id: d.info?.url.href.replace(/.*\/private\/products\//, "") || "", id: d.info?.url.replace(/.*\/private\/products\//, "") || "",
}; };
}); });
return { ok: true, data: dataWithId }; return { ok: true, data: dataWithId };
@ -132,12 +137,15 @@ export function useInstanceProducts(): HttpResponse<
export function useProductDetails( export function useProductDetails(
productId: string, productId: string,
): HttpResponse<MerchantBackend.Products.ProductDetail> { ): HttpResponse<
MerchantBackend.Products.ProductDetail,
MerchantBackend.ErrorDetail
> {
const { fetcher } = useBackendInstanceRequest(); const { fetcher } = useBackendInstanceRequest();
const { data, error, isValidating } = useSWR< const { data, error, isValidating } = useSWR<
HttpResponseOk<MerchantBackend.Products.ProductDetail>, HttpResponseOk<MerchantBackend.Products.ProductDetail>,
HttpError HttpError<MerchantBackend.ErrorDetail>
>([`/private/products/${productId}`], fetcher, { >([`/private/products/${productId}`], fetcher, {
refreshInterval: 0, refreshInterval: 0,
refreshWhenHidden: false, refreshWhenHidden: false,

View File

@ -15,7 +15,11 @@
*/ */
import useSWR, { useSWRConfig } from "swr"; import useSWR, { useSWRConfig } from "swr";
import { MerchantBackend } from "../declaration.js"; import { MerchantBackend } from "../declaration.js";
import { HttpError, HttpResponse, HttpResponseOk } from "../utils/request.js"; import {
HttpError,
HttpResponse,
HttpResponseOk,
} from "@gnu-taler/web-util/lib/index.browser";
import { useBackendInstanceRequest, useMatchMutate } from "./backend.js"; import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
export function useReservesAPI(): ReserveMutateAPI { export function useReservesAPI(): ReserveMutateAPI {
@ -77,7 +81,9 @@ export function useReservesAPI(): ReserveMutateAPI {
return res; return res;
}; };
const deleteReserve = async (pub: string): Promise<HttpResponse<void>> => { const deleteReserve = async (
pub: string,
): Promise<HttpResponse<void, MerchantBackend.ErrorDetail>> => {
const res = await request<void>(`/private/reserves/${pub}`, { const res = await request<void>(`/private/reserves/${pub}`, {
method: "DELETE", method: "DELETE",
}); });
@ -102,15 +108,20 @@ export interface ReserveMutateAPI {
authorizeTip: ( authorizeTip: (
data: MerchantBackend.Tips.TipCreateRequest, data: MerchantBackend.Tips.TipCreateRequest,
) => Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>>; ) => Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>>;
deleteReserve: (id: string) => Promise<HttpResponse<void>>; deleteReserve: (
id: string,
) => Promise<HttpResponse<void, MerchantBackend.ErrorDetail>>;
} }
export function useInstanceReserves(): HttpResponse<MerchantBackend.Tips.TippingReserveStatus> { export function useInstanceReserves(): HttpResponse<
MerchantBackend.Tips.TippingReserveStatus,
MerchantBackend.ErrorDetail
> {
const { fetcher } = useBackendInstanceRequest(); const { fetcher } = useBackendInstanceRequest();
const { data, error, isValidating } = useSWR< const { data, error, isValidating } = useSWR<
HttpResponseOk<MerchantBackend.Tips.TippingReserveStatus>, HttpResponseOk<MerchantBackend.Tips.TippingReserveStatus>,
HttpError HttpError<MerchantBackend.ErrorDetail>
>([`/private/reserves`], fetcher); >([`/private/reserves`], fetcher);
if (isValidating) return { loading: true, data: data?.data }; if (isValidating) return { loading: true, data: data?.data };
@ -121,12 +132,15 @@ export function useInstanceReserves(): HttpResponse<MerchantBackend.Tips.Tipping
export function useReserveDetails( export function useReserveDetails(
reserveId: string, reserveId: string,
): HttpResponse<MerchantBackend.Tips.ReserveDetail> { ): HttpResponse<
MerchantBackend.Tips.ReserveDetail,
MerchantBackend.ErrorDetail
> {
const { reserveDetailFetcher } = useBackendInstanceRequest(); const { reserveDetailFetcher } = useBackendInstanceRequest();
const { data, error, isValidating } = useSWR< const { data, error, isValidating } = useSWR<
HttpResponseOk<MerchantBackend.Tips.ReserveDetail>, HttpResponseOk<MerchantBackend.Tips.ReserveDetail>,
HttpError HttpError<MerchantBackend.ErrorDetail>
>([`/private/reserves/${reserveId}`], reserveDetailFetcher, { >([`/private/reserves/${reserveId}`], reserveDetailFetcher, {
refreshInterval: 0, refreshInterval: 0,
refreshWhenHidden: false, refreshWhenHidden: false,
@ -143,12 +157,12 @@ export function useReserveDetails(
export function useTipDetails( export function useTipDetails(
tipId: string, tipId: string,
): HttpResponse<MerchantBackend.Tips.TipDetails> { ): HttpResponse<MerchantBackend.Tips.TipDetails, MerchantBackend.ErrorDetail> {
const { tipsDetailFetcher } = useBackendInstanceRequest(); const { tipsDetailFetcher } = useBackendInstanceRequest();
const { data, error, isValidating } = useSWR< const { data, error, isValidating } = useSWR<
HttpResponseOk<MerchantBackend.Tips.TipDetails>, HttpResponseOk<MerchantBackend.Tips.TipDetails>,
HttpError HttpError<MerchantBackend.ErrorDetail>
>([`/private/tips/${tipId}`], tipsDetailFetcher, { >([`/private/tips/${tipId}`], tipsDetailFetcher, {
refreshInterval: 0, refreshInterval: 0,
refreshWhenHidden: false, refreshWhenHidden: false,

View File

@ -23,7 +23,7 @@ import {
HttpResponse, HttpResponse,
HttpResponseOk, HttpResponseOk,
HttpResponsePaginated, HttpResponsePaginated,
} from "../utils/request.js"; } from "@gnu-taler/web-util/lib/index.browser";
export function useTemplateAPI(): TemplateAPI { export function useTemplateAPI(): TemplateAPI {
const mutateAll = useMatchMutate(); const mutateAll = useMatchMutate();
@ -79,7 +79,12 @@ export function useTemplateAPI(): TemplateAPI {
return res; return res;
}; };
return { createTemplate, updateTemplate, deleteTemplate, createOrderFromTemplate }; return {
createTemplate,
updateTemplate,
deleteTemplate,
createOrderFromTemplate,
};
} }
export interface TemplateAPI { export interface TemplateAPI {
@ -105,7 +110,10 @@ export interface InstanceTemplateFilter {
export function useInstanceTemplates( export function useInstanceTemplates(
args?: InstanceTemplateFilter, args?: InstanceTemplateFilter,
updatePosition?: (id: string) => void, updatePosition?: (id: string) => void,
): HttpResponsePaginated<MerchantBackend.Template.TemplateSummaryResponse> { ): HttpResponsePaginated<
MerchantBackend.Template.TemplateSummaryResponse,
MerchantBackend.ErrorDetail
> {
const { templateFetcher } = useBackendInstanceRequest(); const { templateFetcher } = useBackendInstanceRequest();
// const [pageBefore, setPageBefore] = useState(1); // const [pageBefore, setPageBefore] = useState(1);
@ -140,15 +148,18 @@ export function useInstanceTemplates(
isValidating: loadingAfter, isValidating: loadingAfter,
} = useSWR< } = useSWR<
HttpResponseOk<MerchantBackend.Template.TemplateSummaryResponse>, HttpResponseOk<MerchantBackend.Template.TemplateSummaryResponse>,
HttpError HttpError<MerchantBackend.ErrorDetail>
>([`/private/templates`, args?.position, -totalAfter], templateFetcher); >([`/private/templates`, args?.position, -totalAfter], templateFetcher);
//this will save last result //this will save last result
// const [lastBefore, setLastBefore] = useState< // const [lastBefore, setLastBefore] = useState<
// HttpResponse<MerchantBackend.Template.TemplateSummaryResponse> // HttpResponse<MerchantBackend.Template.TemplateSummaryResponse, MerchantBackend.ErrorDetail>
// >({ loading: true }); // >({ loading: true });
const [lastAfter, setLastAfter] = useState< const [lastAfter, setLastAfter] = useState<
HttpResponse<MerchantBackend.Template.TemplateSummaryResponse> HttpResponse<
MerchantBackend.Template.TemplateSummaryResponse,
MerchantBackend.ErrorDetail
>
>({ loading: true }); >({ loading: true });
useEffect(() => { useEffect(() => {
if (afterData) setLastAfter(afterData); if (afterData) setLastAfter(afterData);
@ -174,9 +185,10 @@ export function useInstanceTemplates(
if (afterData.data.templates.length < MAX_RESULT_SIZE) { if (afterData.data.templates.length < MAX_RESULT_SIZE) {
setPageAfter(pageAfter + 1); setPageAfter(pageAfter + 1);
} else { } else {
const from = `${afterData.data.templates[afterData.data.templates.length - 1] const from = `${
.template_id afterData.data.templates[afterData.data.templates.length - 1]
}`; .template_id
}`;
if (from && updatePosition) updatePosition(from); if (from && updatePosition) updatePosition(from);
} }
}, },
@ -211,12 +223,15 @@ export function useInstanceTemplates(
export function useTemplateDetails( export function useTemplateDetails(
templateId: string, templateId: string,
): HttpResponse<MerchantBackend.Template.TemplateDetails> { ): HttpResponse<
MerchantBackend.Template.TemplateDetails,
MerchantBackend.ErrorDetail
> {
const { templateFetcher } = useBackendInstanceRequest(); const { templateFetcher } = useBackendInstanceRequest();
const { data, error, isValidating } = useSWR< const { data, error, isValidating } = useSWR<
HttpResponseOk<MerchantBackend.Template.TemplateDetails>, HttpResponseOk<MerchantBackend.Template.TemplateDetails>,
HttpError HttpError<MerchantBackend.ErrorDetail>
>([`/private/templates/${templateId}`], templateFetcher, { >([`/private/templates/${templateId}`], templateFetcher, {
refreshInterval: 0, refreshInterval: 0,
refreshWhenHidden: false, refreshWhenHidden: false,

View File

@ -22,10 +22,13 @@
import { MockEnvironment } from "@gnu-taler/web-util/lib/tests/mock"; import { MockEnvironment } from "@gnu-taler/web-util/lib/tests/mock";
import { ComponentChildren, FunctionalComponent, h, VNode } from "preact"; import { ComponentChildren, FunctionalComponent, h, VNode } from "preact";
import { SWRConfig } from "swr"; import { SWRConfig } from "swr";
import { ApiContextProvider } from "../context/api.js"; import { ApiContextProvider } from "@gnu-taler/web-util/lib/index.browser";
import { BackendContextProvider } from "../context/backend.js"; import { BackendContextProvider } from "../context/backend.js";
import { InstanceContextProvider } from "../context/instance.js"; import { InstanceContextProvider } from "../context/instance.js";
import { HttpResponseOk, RequestOptions } from "../utils/request.js"; import {
HttpResponseOk,
RequestOptions,
} from "@gnu-taler/web-util/lib/index.browser";
export class ApiMockEnvironment extends MockEnvironment { export class ApiMockEnvironment extends MockEnvironment {
constructor(debug = false) { constructor(debug = false) {
@ -78,7 +81,7 @@ export class ApiMockEnvironment extends MockEnvironment {
info: { info: {
hasToken: !!options.token, hasToken: !!options.token,
status: !mocked ? 200 : mocked.status, status: !mocked ? 200 : mocked.status,
url: _url, url: _url.href,
payload: options.data, payload: options.data,
}, },
}; };

View File

@ -22,7 +22,7 @@ import {
HttpResponse, HttpResponse,
HttpResponseOk, HttpResponseOk,
HttpResponsePaginated, HttpResponsePaginated,
} from "../utils/request.js"; } from "@gnu-taler/web-util/lib/index.browser";
import { useBackendInstanceRequest, useMatchMutate } from "./backend.js"; import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
export function useTransferAPI(): TransferAPI { export function useTransferAPI(): TransferAPI {
@ -67,7 +67,10 @@ export interface InstanceTransferFilter {
export function useInstanceTransfers( export function useInstanceTransfers(
args?: InstanceTransferFilter, args?: InstanceTransferFilter,
updatePosition?: (id: string) => void, updatePosition?: (id: string) => void,
): HttpResponsePaginated<MerchantBackend.Transfers.TransferList> { ): HttpResponsePaginated<
MerchantBackend.Transfers.TransferList,
MerchantBackend.ErrorDetail
> {
const { transferFetcher } = useBackendInstanceRequest(); const { transferFetcher } = useBackendInstanceRequest();
const [pageBefore, setPageBefore] = useState(1); const [pageBefore, setPageBefore] = useState(1);
@ -86,7 +89,10 @@ export function useInstanceTransfers(
data: beforeData, data: beforeData,
error: beforeError, error: beforeError,
isValidating: loadingBefore, isValidating: loadingBefore,
} = useSWR<HttpResponseOk<MerchantBackend.Transfers.TransferList>, HttpError>( } = useSWR<
HttpResponseOk<MerchantBackend.Transfers.TransferList>,
HttpError<MerchantBackend.ErrorDetail>
>(
[ [
`/private/transfers`, `/private/transfers`,
args?.payto_uri, args?.payto_uri,
@ -100,7 +106,10 @@ export function useInstanceTransfers(
data: afterData, data: afterData,
error: afterError, error: afterError,
isValidating: loadingAfter, isValidating: loadingAfter,
} = useSWR<HttpResponseOk<MerchantBackend.Transfers.TransferList>, HttpError>( } = useSWR<
HttpResponseOk<MerchantBackend.Transfers.TransferList>,
HttpError<MerchantBackend.ErrorDetail>
>(
[ [
`/private/transfers`, `/private/transfers`,
args?.payto_uri, args?.payto_uri,
@ -113,10 +122,16 @@ export function useInstanceTransfers(
//this will save last result //this will save last result
const [lastBefore, setLastBefore] = useState< const [lastBefore, setLastBefore] = useState<
HttpResponse<MerchantBackend.Transfers.TransferList> HttpResponse<
MerchantBackend.Transfers.TransferList,
MerchantBackend.ErrorDetail
>
>({ loading: true }); >({ loading: true });
const [lastAfter, setLastAfter] = useState< const [lastAfter, setLastAfter] = useState<
HttpResponse<MerchantBackend.Transfers.TransferList> HttpResponse<
MerchantBackend.Transfers.TransferList,
MerchantBackend.ErrorDetail
>
>({ loading: true }); >({ loading: true });
useEffect(() => { useEffect(() => {
if (afterData) setLastAfter(afterData); if (afterData) setLastAfter(afterData);

View File

@ -23,7 +23,7 @@ import {
HttpResponse, HttpResponse,
HttpResponseOk, HttpResponseOk,
HttpResponsePaginated, HttpResponsePaginated,
} from "../utils/request.js"; } from "@gnu-taler/web-util/lib/index.browser";
export function useWebhookAPI(): WebhookAPI { export function useWebhookAPI(): WebhookAPI {
const mutateAll = useMatchMutate(); const mutateAll = useMatchMutate();
@ -84,7 +84,10 @@ export interface InstanceWebhookFilter {
export function useInstanceWebhooks( export function useInstanceWebhooks(
args?: InstanceWebhookFilter, args?: InstanceWebhookFilter,
updatePosition?: (id: string) => void, updatePosition?: (id: string) => void,
): HttpResponsePaginated<MerchantBackend.Webhooks.WebhookSummaryResponse> { ): HttpResponsePaginated<
MerchantBackend.Webhooks.WebhookSummaryResponse,
MerchantBackend.ErrorDetail
> {
const { webhookFetcher } = useBackendInstanceRequest(); const { webhookFetcher } = useBackendInstanceRequest();
const [pageAfter, setPageAfter] = useState(1); const [pageAfter, setPageAfter] = useState(1);
@ -97,11 +100,14 @@ export function useInstanceWebhooks(
isValidating: loadingAfter, isValidating: loadingAfter,
} = useSWR< } = useSWR<
HttpResponseOk<MerchantBackend.Webhooks.WebhookSummaryResponse>, HttpResponseOk<MerchantBackend.Webhooks.WebhookSummaryResponse>,
HttpError HttpError<MerchantBackend.ErrorDetail>
>([`/private/webhooks`, args?.position, -totalAfter], webhookFetcher); >([`/private/webhooks`, args?.position, -totalAfter], webhookFetcher);
const [lastAfter, setLastAfter] = useState< const [lastAfter, setLastAfter] = useState<
HttpResponse<MerchantBackend.Webhooks.WebhookSummaryResponse> HttpResponse<
MerchantBackend.Webhooks.WebhookSummaryResponse,
MerchantBackend.ErrorDetail
>
>({ loading: true }); >({ loading: true });
useEffect(() => { useEffect(() => {
if (afterData) setLastAfter(afterData); if (afterData) setLastAfter(afterData);
@ -121,21 +127,20 @@ export function useInstanceWebhooks(
if (afterData.data.webhooks.length < MAX_RESULT_SIZE) { if (afterData.data.webhooks.length < MAX_RESULT_SIZE) {
setPageAfter(pageAfter + 1); setPageAfter(pageAfter + 1);
} else { } else {
const from = `${afterData.data.webhooks[afterData.data.webhooks.length - 1] const from = `${
.webhook_id afterData.data.webhooks[afterData.data.webhooks.length - 1].webhook_id
}`; }`;
if (from && updatePosition) updatePosition(from); if (from && updatePosition) updatePosition(from);
} }
}, },
loadMorePrev: () => { loadMorePrev: () => {
return return;
}, },
}; };
const webhooks = !afterData ? [] : (afterData || lastAfter).data.webhooks; const webhooks = !afterData ? [] : (afterData || lastAfter).data.webhooks;
if (loadingAfter) if (loadingAfter) return { loading: true, data: { webhooks } };
return { loading: true, data: { webhooks } };
if (afterData) { if (afterData) {
return { ok: true, data: { webhooks }, ...pagination }; return { ok: true, data: { webhooks }, ...pagination };
} }
@ -144,12 +149,15 @@ export function useInstanceWebhooks(
export function useWebhookDetails( export function useWebhookDetails(
webhookId: string, webhookId: string,
): HttpResponse<MerchantBackend.Webhooks.WebhookDetails> { ): HttpResponse<
MerchantBackend.Webhooks.WebhookDetails,
MerchantBackend.ErrorDetail
> {
const { webhookFetcher } = useBackendInstanceRequest(); const { webhookFetcher } = useBackendInstanceRequest();
const { data, error, isValidating } = useSWR< const { data, error, isValidating } = useSWR<
HttpResponseOk<MerchantBackend.Webhooks.WebhookDetails>, HttpResponseOk<MerchantBackend.Webhooks.WebhookDetails>,
HttpError HttpError<MerchantBackend.ErrorDetail>
>([`/private/webhooks/${webhookId}`], webhookFetcher, { >([`/private/webhooks/${webhookId}`], webhookFetcher, {
refreshInterval: 0, refreshInterval: 0,
refreshWhenHidden: false, refreshWhenHidden: false,

View File

@ -19,14 +19,16 @@
* @author Sebastian Javier Marchano (sebasjm) * @author Sebastian Javier Marchano (sebasjm)
*/ */
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; import {
HttpError,
useTranslationContext,
} from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { Loading } from "../../../components/exception/loading.js"; import { Loading } from "../../../components/exception/loading.js";
import { NotificationCard } from "../../../components/menu/index.js"; import { NotificationCard } from "../../../components/menu/index.js";
import { DeleteModal, PurgeModal } from "../../../components/modal/index.js"; import { DeleteModal, PurgeModal } from "../../../components/modal/index.js";
import { MerchantBackend } from "../../../declaration.js"; import { MerchantBackend } from "../../../declaration.js";
import { HttpError } from "../../../utils/request.js";
import { useAdminAPI, useBackendInstances } from "../../../hooks/instance.js"; import { useAdminAPI, useBackendInstances } from "../../../hooks/instance.js";
import { Notification } from "../../../utils/types.js"; import { Notification } from "../../../utils/types.js";
import { View } from "./View.js"; import { View } from "./View.js";
@ -37,7 +39,7 @@ interface Props {
instances: MerchantBackend.Instances.Instance[]; instances: MerchantBackend.Instances.Instance[];
onUnauthorized: () => VNode; onUnauthorized: () => VNode;
onNotFound: () => VNode; onNotFound: () => VNode;
onLoadError: (error: HttpError) => VNode; onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
setInstanceName: (s: string) => void; setInstanceName: (s: string) => void;
} }

View File

@ -13,18 +13,19 @@
You should have received a copy of the GNU General Public License along with 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/> GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { HttpError } from "@gnu-taler/web-util/lib/index.browser.js";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { Loading } from "../../../components/exception/loading.js"; import { Loading } from "../../../components/exception/loading.js";
import { DeleteModal } from "../../../components/modal/index.js"; import { DeleteModal } from "../../../components/modal/index.js";
import { useInstanceContext } from "../../../context/instance.js"; import { useInstanceContext } from "../../../context/instance.js";
import { HttpError } from "../../../utils/request.js"; import { MerchantBackend } from "../../../declaration.js";
import { useInstanceAPI, useInstanceDetails } from "../../../hooks/instance.js"; import { useInstanceAPI, useInstanceDetails } from "../../../hooks/instance.js";
import { DetailPage } from "./DetailPage.js"; import { DetailPage } from "./DetailPage.js";
interface Props { interface Props {
onUnauthorized: () => VNode; onUnauthorized: () => VNode;
onLoadError: (error: HttpError) => VNode; onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
onUpdate: () => void; onUpdate: () => void;
onNotFound: () => VNode; onNotFound: () => VNode;
onDelete: () => void; onDelete: () => void;
@ -63,7 +64,9 @@ export default function Detail({
try { try {
await deleteInstance(); await deleteInstance();
onDelete(); onDelete();
} catch (error) {} } catch (error) {
//FIXME: show message error
}
setDeleting(false); setDeleting(false);
}} }}
/> />

View File

@ -19,15 +19,16 @@
* @author Sebastian Javier Marchano (sebasjm) * @author Sebastian Javier Marchano (sebasjm)
*/ */
import { HttpError } from "@gnu-taler/web-util/lib/index.browser.js";
import { h, VNode } from "preact"; import { h, VNode } from "preact";
import { Loading } from "../../../../components/exception/loading.js"; import { Loading } from "../../../../components/exception/loading.js";
import { HttpError } from "../../../../utils/request.js"; import { MerchantBackend } from "../../../../declaration.js";
import { useInstanceKYCDetails } from "../../../../hooks/instance.js"; import { useInstanceKYCDetails } from "../../../../hooks/instance.js";
import { ListPage } from "./ListPage.js"; import { ListPage } from "./ListPage.js";
interface Props { interface Props {
onUnauthorized: () => VNode; onUnauthorized: () => VNode;
onLoadError: (error: HttpError) => VNode; onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
onNotFound: () => VNode; onNotFound: () => VNode;
} }

View File

@ -19,18 +19,17 @@
* @author Sebastian Javier Marchano (sebasjm) * @author Sebastian Javier Marchano (sebasjm)
*/ */
import { HttpError } from "@gnu-taler/web-util/lib/index.browser.js";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading.js"; import { Loading } from "../../../../components/exception/loading.js";
import { NotificationCard } from "../../../../components/menu/index.js"; import { NotificationCard } from "../../../../components/menu/index.js";
import { MerchantBackend } from "../../../../declaration.js"; import { MerchantBackend } from "../../../../declaration.js";
import { HttpError } from "../../../../utils/request.js";
import { useInstanceDetails } from "../../../../hooks/instance.js"; import { useInstanceDetails } from "../../../../hooks/instance.js";
import { useOrderAPI } from "../../../../hooks/order.js"; import { useOrderAPI } from "../../../../hooks/order.js";
import { useInstanceProducts } from "../../../../hooks/product.js"; import { useInstanceProducts } from "../../../../hooks/product.js";
import { Notification } from "../../../../utils/types.js"; import { Notification } from "../../../../utils/types.js";
import { CreatePage } from "./CreatePage.js"; import { CreatePage } from "./CreatePage.js";
import { OrderCreatedSuccessfully } from "./OrderCreatedSuccessfully.js";
export type Entity = { export type Entity = {
request: MerchantBackend.Orders.PostOrderRequest; request: MerchantBackend.Orders.PostOrderRequest;
@ -41,7 +40,7 @@ interface Props {
onConfirm: () => void; onConfirm: () => void;
onUnauthorized: () => VNode; onUnauthorized: () => VNode;
onNotFound: () => VNode; onNotFound: () => VNode;
onLoadError: (error: HttpError) => VNode; onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
} }
export default function OrderCreate({ export default function OrderCreate({
onConfirm, onConfirm,

View File

@ -13,12 +13,15 @@
You should have received a copy of the GNU General Public License along with 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/> GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; import {
useTranslationContext,
HttpError,
} from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading.js"; import { Loading } from "../../../../components/exception/loading.js";
import { NotificationCard } from "../../../../components/menu/index.js"; import { NotificationCard } from "../../../../components/menu/index.js";
import { HttpError } from "../../../../utils/request.js"; import { MerchantBackend } from "../../../../declaration.js";
import { useOrderAPI, useOrderDetails } from "../../../../hooks/order.js"; import { useOrderAPI, useOrderDetails } from "../../../../hooks/order.js";
import { Notification } from "../../../../utils/types.js"; import { Notification } from "../../../../utils/types.js";
import { DetailPage } from "./DetailPage.js"; import { DetailPage } from "./DetailPage.js";
@ -29,7 +32,7 @@ export interface Props {
onBack: () => void; onBack: () => void;
onUnauthorized: () => VNode; onUnauthorized: () => VNode;
onNotFound: () => VNode; onNotFound: () => VNode;
onLoadError: (error: HttpError) => VNode; onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
} }
export default function Update({ export default function Update({

View File

@ -19,13 +19,15 @@
* @author Sebastian Javier Marchano (sebasjm) * @author Sebastian Javier Marchano (sebasjm)
*/ */
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; import {
HttpError,
useTranslationContext,
} from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading.js"; import { Loading } from "../../../../components/exception/loading.js";
import { NotificationCard } from "../../../../components/menu/index.js"; import { NotificationCard } from "../../../../components/menu/index.js";
import { MerchantBackend } from "../../../../declaration.js"; import { MerchantBackend } from "../../../../declaration.js";
import { HttpError } from "../../../../utils/request.js";
import { import {
InstanceOrderFilter, InstanceOrderFilter,
useInstanceOrders, useInstanceOrders,
@ -38,7 +40,7 @@ import { RefundModal } from "./Table.js";
interface Props { interface Props {
onUnauthorized: () => VNode; onUnauthorized: () => VNode;
onLoadError: (error: HttpError) => VNode; onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
onNotFound: () => VNode; onNotFound: () => VNode;
onSelect: (id: string) => void; onSelect: (id: string) => void;
onCreate: () => void; onCreate: () => void;
@ -177,7 +179,7 @@ export default function OrderList({
interface RefundProps { interface RefundProps {
id: string; id: string;
onUnauthorized: () => VNode; onUnauthorized: () => VNode;
onLoadError: (error: HttpError) => VNode; onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
onNotFound: () => VNode; onNotFound: () => VNode;
onCancel: () => void; onCancel: () => void;
onConfirm: (m: MerchantBackend.Orders.RefundRequest) => void; onConfirm: (m: MerchantBackend.Orders.RefundRequest) => void;

View File

@ -19,13 +19,15 @@
* @author Sebastian Javier Marchano (sebasjm) * @author Sebastian Javier Marchano (sebasjm)
*/ */
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; import {
HttpError,
useTranslationContext,
} from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact"; import { h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading.js"; import { Loading } from "../../../../components/exception/loading.js";
import { NotificationCard } from "../../../../components/menu/index.js"; import { NotificationCard } from "../../../../components/menu/index.js";
import { MerchantBackend, WithId } from "../../../../declaration.js"; import { MerchantBackend, WithId } from "../../../../declaration.js";
import { HttpError } from "../../../../utils/request.js";
import { import {
useInstanceProducts, useInstanceProducts,
useProductAPI, useProductAPI,
@ -38,7 +40,7 @@ interface Props {
onNotFound: () => VNode; onNotFound: () => VNode;
onCreate: () => void; onCreate: () => void;
onSelect: (id: string) => void; onSelect: (id: string) => void;
onLoadError: (e: HttpError) => VNode; onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
} }
export default function ProductList({ export default function ProductList({
onUnauthorized, onUnauthorized,

View File

@ -19,13 +19,15 @@
* @author Sebastian Javier Marchano (sebasjm) * @author Sebastian Javier Marchano (sebasjm)
*/ */
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; import {
HttpError,
useTranslationContext,
} from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading.js"; import { Loading } from "../../../../components/exception/loading.js";
import { NotificationCard } from "../../../../components/menu/index.js"; import { NotificationCard } from "../../../../components/menu/index.js";
import { MerchantBackend } from "../../../../declaration.js"; import { MerchantBackend } from "../../../../declaration.js";
import { HttpError } from "../../../../utils/request.js";
import { useProductAPI, useProductDetails } from "../../../../hooks/product.js"; import { useProductAPI, useProductDetails } from "../../../../hooks/product.js";
import { Notification } from "../../../../utils/types.js"; import { Notification } from "../../../../utils/types.js";
import { UpdatePage } from "./UpdatePage.js"; import { UpdatePage } from "./UpdatePage.js";
@ -36,7 +38,7 @@ interface Props {
onConfirm: () => void; onConfirm: () => void;
onUnauthorized: () => VNode; onUnauthorized: () => VNode;
onNotFound: () => VNode; onNotFound: () => VNode;
onLoadError: (e: HttpError) => VNode; onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
pid: string; pid: string;
} }
export default function UpdateProduct({ export default function UpdateProduct({

View File

@ -30,8 +30,7 @@ import {
import { Input } from "../../../../components/form/Input.js"; import { Input } from "../../../../components/form/Input.js";
import { InputCurrency } from "../../../../components/form/InputCurrency.js"; import { InputCurrency } from "../../../../components/form/InputCurrency.js";
import { InputSelector } from "../../../../components/form/InputSelector.js"; import { InputSelector } from "../../../../components/form/InputSelector.js";
import { ExchangeBackend, MerchantBackend } from "../../../../declaration.js"; import { MerchantBackend } from "../../../../declaration.js";
// import { request } from "../../../../utils/request.js";
import { import {
PAYTO_WIRE_METHOD_LOOKUP, PAYTO_WIRE_METHOD_LOOKUP,
URL_REGEX, URL_REGEX,

View File

@ -19,9 +19,10 @@
* @author Sebastian Javier Marchano (sebasjm) * @author Sebastian Javier Marchano (sebasjm)
*/ */
import { HttpError } from "@gnu-taler/web-util/lib/index.browser.js";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { Loading } from "../../../../components/exception/loading.js"; import { Loading } from "../../../../components/exception/loading.js";
import { HttpError } from "../../../../utils/request.js"; import { MerchantBackend } from "../../../../declaration.js";
import { useReserveDetails } from "../../../../hooks/reserves.js"; import { useReserveDetails } from "../../../../hooks/reserves.js";
import { DetailPage } from "./DetailPage.js"; import { DetailPage } from "./DetailPage.js";
@ -29,7 +30,7 @@ interface Props {
rid: string; rid: string;
onUnauthorized: () => VNode; onUnauthorized: () => VNode;
onLoadError: (error: HttpError) => VNode; onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
onNotFound: () => VNode; onNotFound: () => VNode;
onDelete: () => void; onDelete: () => void;
onBack: () => void; onBack: () => void;

View File

@ -19,13 +19,15 @@
* @author Sebastian Javier Marchano (sebasjm) * @author Sebastian Javier Marchano (sebasjm)
*/ */
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; import {
HttpError,
useTranslationContext,
} from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact"; import { h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading.js"; import { Loading } from "../../../../components/exception/loading.js";
import { NotificationCard } from "../../../../components/menu/index.js"; import { NotificationCard } from "../../../../components/menu/index.js";
import { MerchantBackend } from "../../../../declaration.js"; import { MerchantBackend } from "../../../../declaration.js";
import { HttpError } from "../../../../utils/request.js";
import { import {
useInstanceReserves, useInstanceReserves,
useReservesAPI, useReservesAPI,
@ -36,7 +38,7 @@ import { CardTable } from "./Table.js";
interface Props { interface Props {
onUnauthorized: () => VNode; onUnauthorized: () => VNode;
onLoadError: (e: HttpError) => VNode; onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
onSelect: (id: string) => void; onSelect: (id: string) => void;
onNotFound: () => VNode; onNotFound: () => VNode;
onCreate: () => void; onCreate: () => void;

View File

@ -19,13 +19,15 @@
* @author Sebastian Javier Marchano (sebasjm) * @author Sebastian Javier Marchano (sebasjm)
*/ */
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; import {
HttpError,
useTranslationContext,
} from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading.js"; import { Loading } from "../../../../components/exception/loading.js";
import { NotificationCard } from "../../../../components/menu/index.js"; import { NotificationCard } from "../../../../components/menu/index.js";
import { MerchantBackend } from "../../../../declaration.js"; import { MerchantBackend } from "../../../../declaration.js";
import { HttpError } from "../../../../utils/request.js";
import { import {
useInstanceTemplates, useInstanceTemplates,
useTemplateAPI, useTemplateAPI,
@ -35,7 +37,7 @@ import { ListPage } from "./ListPage.js";
interface Props { interface Props {
onUnauthorized: () => VNode; onUnauthorized: () => VNode;
onLoadError: (error: HttpError) => VNode; onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
onNotFound: () => VNode; onNotFound: () => VNode;
onCreate: () => void; onCreate: () => void;
onSelect: (id: string) => void; onSelect: (id: string) => void;

View File

@ -19,13 +19,15 @@
* @author Sebastian Javier Marchano (sebasjm) * @author Sebastian Javier Marchano (sebasjm)
*/ */
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; import {
HttpError,
useTranslationContext,
} from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading.js"; import { Loading } from "../../../../components/exception/loading.js";
import { NotificationCard } from "../../../../components/menu/index.js"; import { NotificationCard } from "../../../../components/menu/index.js";
import { MerchantBackend, WithId } from "../../../../declaration.js"; import { MerchantBackend, WithId } from "../../../../declaration.js";
import { HttpError } from "../../../../utils/request.js";
import { import {
useTemplateAPI, useTemplateAPI,
useTemplateDetails, useTemplateDetails,
@ -40,7 +42,7 @@ interface Props {
onConfirm: () => void; onConfirm: () => void;
onUnauthorized: () => VNode; onUnauthorized: () => VNode;
onNotFound: () => VNode; onNotFound: () => VNode;
onLoadError: (e: HttpError) => VNode; onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
tid: string; tid: string;
} }
export default function UpdateTemplate({ export default function UpdateTemplate({

View File

@ -19,7 +19,6 @@
* @author Sebastian Javier Marchano (sebasjm) * @author Sebastian Javier Marchano (sebasjm)
*/ */
import { h, VNode, FunctionalComponent } from "preact";
import { UsePage as TestedComponent } from "./UsePage.js"; import { UsePage as TestedComponent } from "./UsePage.js";
export default { export default {

View File

@ -19,7 +19,10 @@
* @author Sebastian Javier Marchano (sebasjm) * @author Sebastian Javier Marchano (sebasjm)
*/ */
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; import {
HttpError,
useTranslationContext,
} from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading.js"; import { Loading } from "../../../../components/exception/loading.js";
@ -29,7 +32,6 @@ import {
useTemplateAPI, useTemplateAPI,
useTemplateDetails, useTemplateDetails,
} from "../../../../hooks/templates.js"; } from "../../../../hooks/templates.js";
import { HttpError } from "../../../../utils/request.js";
import { Notification } from "../../../../utils/types.js"; import { Notification } from "../../../../utils/types.js";
import { UsePage } from "./UsePage.js"; import { UsePage } from "./UsePage.js";
@ -39,7 +41,7 @@ interface Props {
onOrderCreated: (id: string) => void; onOrderCreated: (id: string) => void;
onUnauthorized: () => VNode; onUnauthorized: () => VNode;
onNotFound: () => VNode; onNotFound: () => VNode;
onLoadError: (e: HttpError) => VNode; onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
tid: string; tid: string;
} }

View File

@ -19,18 +19,18 @@
* @author Sebastian Javier Marchano (sebasjm) * @author Sebastian Javier Marchano (sebasjm)
*/ */
import { HttpError } from "@gnu-taler/web-util/lib/index.browser.js";
import { h, VNode } from "preact"; import { h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading.js"; import { Loading } from "../../../../components/exception/loading.js";
import { MerchantBackend } from "../../../../declaration.js"; import { MerchantBackend } from "../../../../declaration.js";
import { HttpError } from "../../../../utils/request.js";
import { useInstanceDetails } from "../../../../hooks/instance.js"; import { useInstanceDetails } from "../../../../hooks/instance.js";
import { useInstanceTransfers } from "../../../../hooks/transfer.js"; import { useInstanceTransfers } from "../../../../hooks/transfer.js";
import { ListPage } from "./ListPage.js"; import { ListPage } from "./ListPage.js";
interface Props { interface Props {
onUnauthorized: () => VNode; onUnauthorized: () => VNode;
onLoadError: (error: HttpError) => VNode; onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
onNotFound: () => VNode; onNotFound: () => VNode;
onCreate: () => void; onCreate: () => void;
} }

View File

@ -13,7 +13,11 @@
You should have received a copy of the GNU General Public License along with 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/> GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; import {
HttpError,
HttpResponse,
useTranslationContext,
} from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { Loading } from "../../../components/exception/loading.js"; import { Loading } from "../../../components/exception/loading.js";
@ -26,7 +30,6 @@ import {
useManagedInstanceDetails, useManagedInstanceDetails,
useManagementAPI, useManagementAPI,
} from "../../../hooks/instance.js"; } from "../../../hooks/instance.js";
import { HttpError, HttpResponse } from "../../../utils/request.js";
import { Notification } from "../../../utils/types.js"; import { Notification } from "../../../utils/types.js";
import { UpdatePage } from "./UpdatePage.js"; import { UpdatePage } from "./UpdatePage.js";
@ -36,8 +39,8 @@ export interface Props {
onUnauthorized: () => VNode; onUnauthorized: () => VNode;
onNotFound: () => VNode; onNotFound: () => VNode;
onLoadError: (e: HttpError) => VNode; onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
onUpdateError: (e: HttpError) => void; onUpdateError: (e: HttpError<MerchantBackend.ErrorDetail>) => void;
} }
export default function Update(props: Props): VNode { export default function Update(props: Props): VNode {
@ -63,7 +66,10 @@ function CommonUpdate(
onUpdateError, onUpdateError,
onUnauthorized, onUnauthorized,
}: Props, }: Props,
result: HttpResponse<MerchantBackend.Instances.QueryInstancesResponse>, result: HttpResponse<
MerchantBackend.Instances.QueryInstancesResponse,
MerchantBackend.ErrorDetail
>,
updateInstance: any, updateInstance: any,
clearToken: any, clearToken: any,
setNewToken: any, setNewToken: any,

View File

@ -19,7 +19,10 @@
* @author Sebastian Javier Marchano (sebasjm) * @author Sebastian Javier Marchano (sebasjm)
*/ */
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; import {
HttpError,
useTranslationContext,
} from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading.js"; import { Loading } from "../../../../components/exception/loading.js";
@ -29,13 +32,12 @@ import {
useInstanceWebhooks, useInstanceWebhooks,
useWebhookAPI, useWebhookAPI,
} from "../../../../hooks/webhooks.js"; } from "../../../../hooks/webhooks.js";
import { HttpError } from "../../../../utils/request.js";
import { Notification } from "../../../../utils/types.js"; import { Notification } from "../../../../utils/types.js";
import { ListPage } from "./ListPage.js"; import { ListPage } from "./ListPage.js";
interface Props { interface Props {
onUnauthorized: () => VNode; onUnauthorized: () => VNode;
onLoadError: (error: HttpError) => VNode; onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
onNotFound: () => VNode; onNotFound: () => VNode;
onCreate: () => void; onCreate: () => void;
onSelect: (id: string) => void; onSelect: (id: string) => void;

View File

@ -19,7 +19,10 @@
* @author Sebastian Javier Marchano (sebasjm) * @author Sebastian Javier Marchano (sebasjm)
*/ */
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; import {
HttpError,
useTranslationContext,
} from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading.js"; import { Loading } from "../../../../components/exception/loading.js";
@ -29,7 +32,6 @@ import {
useWebhookAPI, useWebhookAPI,
useWebhookDetails, useWebhookDetails,
} from "../../../../hooks/webhooks.js"; } from "../../../../hooks/webhooks.js";
import { HttpError } from "../../../../utils/request.js";
import { Notification } from "../../../../utils/types.js"; import { Notification } from "../../../../utils/types.js";
import { UpdatePage } from "./UpdatePage.js"; import { UpdatePage } from "./UpdatePage.js";
@ -40,7 +42,7 @@ interface Props {
onConfirm: () => void; onConfirm: () => void;
onUnauthorized: () => VNode; onUnauthorized: () => VNode;
onNotFound: () => VNode; onNotFound: () => VNode;
onLoadError: (e: HttpError) => VNode; onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
tid: string; tid: string;
} }
export default function UpdateWebhook({ export default function UpdateWebhook({

View File

@ -1,282 +0,0 @@
/*
This file is part of GNU Taler
(C) 2021-2023 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/>
*/
// import axios, { AxiosError, AxiosResponse } from "axios";
import { MerchantBackend } from "../declaration.js";
export async function defaultRequestHandler<T>(
base: string,
path: string,
options: RequestOptions = {},
): Promise<HttpResponseOk<T>> {
const requestHeaders = options.token
? { Authorization: `Bearer ${options.token}` }
: undefined;
const requestMethod = options?.method ?? "GET";
const requestBody = options?.data;
const requestTimeout = 2 * 1000;
const requestParams = options.params ?? {};
const _url = new URL(`${base}${path}`);
Object.entries(requestParams).forEach(([key, value]) => {
_url.searchParams.set(key, String(value));
});
let payload: BodyInit | undefined = undefined;
if (requestBody != null) {
if (typeof requestBody === "string") {
payload = requestBody;
} else if (requestBody instanceof ArrayBuffer) {
payload = requestBody;
} else if (ArrayBuffer.isView(requestBody)) {
payload = requestBody;
} else if (typeof requestBody === "object") {
payload = JSON.stringify(requestBody);
} else {
throw Error("unsupported request body type");
}
}
const controller = new AbortController();
const timeoutId = setTimeout(() => {
controller.abort("HTTP_REQUEST_TIMEOUT");
}, requestTimeout);
const response = await fetch(_url.href, {
headers: {
...requestHeaders,
"Content-Type": "text/plain",
},
method: requestMethod,
credentials: "omit",
mode: "cors",
body: payload,
signal: controller.signal,
});
if (timeoutId) {
clearTimeout(timeoutId);
}
const headerMap = new Headers();
response.headers.forEach((value, key) => {
headerMap.set(key, value);
});
if (response.ok) {
const result = await buildRequestOk<T>(
response,
_url,
payload,
!!options.token,
);
return result;
} else {
const error = await buildRequestFailed(
response,
_url,
payload,
!!options.token,
);
throw error;
}
}
export type HttpResponse<T> =
| HttpResponseOk<T>
| HttpResponseLoading<T>
| HttpError;
export type HttpResponsePaginated<T> =
| HttpResponseOkPaginated<T>
| HttpResponseLoading<T>
| HttpError;
export interface RequestInfo {
url: URL;
hasToken: boolean;
payload: any;
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";
export interface RequestOptions {
method?: Methods;
token?: string;
data?: any;
params?: unknown;
}
async function buildRequestOk<T>(
response: Response,
url: URL,
payload: any,
hasToken: boolean,
): Promise<HttpResponseOk<T>> {
const dataTxt = await response.text();
const data = dataTxt ? JSON.parse(dataTxt) : undefined;
return {
ok: true,
data,
info: {
payload,
url,
hasToken,
status: response.status,
},
};
}
async function buildRequestFailed(
response: Response,
url: URL,
payload: any,
hasToken: boolean,
): Promise<
| HttpResponseClientError
| HttpResponseServerError
| HttpResponseUnexpectedError
> {
const status = response?.status;
const info: RequestInfo = {
payload,
url,
hasToken,
status: status || 0,
};
try {
const dataTxt = await response.text();
const data = dataTxt ? JSON.parse(dataTxt) : undefined;
if (status && status >= 400 && status < 500) {
const error: HttpResponseClientError = {
clientError: true,
isNotfound: status === 404,
isUnauthorized: status === 401,
status,
info,
message: data?.hint,
error: data,
};
return error;
}
if (status && status >= 500 && status < 600) {
const error: HttpResponseServerError = {
serverError: true,
status,
info,
message: `${data?.hint} (code ${data?.code})`,
error: data,
};
return error;
}
return {
info,
status,
error: {},
message: "NOT DEFINED",
};
} catch (ex) {
const error: HttpResponseUnexpectedError = {
info,
status,
error: ex,
message: "NOT DEFINED",
};
throw error;
}
}
// export function isAxiosError<T>(
// error: AxiosError | any,
// ): error is AxiosError<T> {
// return error && error.isAxiosError;
// }