summaryrefslogtreecommitdiff
path: root/packages/merchant-backoffice-ui/src/hooks
diff options
context:
space:
mode:
Diffstat (limited to 'packages/merchant-backoffice-ui/src/hooks')
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/backend.ts245
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/instance.test.ts3
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/instance.ts87
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/preference.ts3
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/session.ts185
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/testing.tsx24
6 files changed, 213 insertions, 334 deletions
diff --git a/packages/merchant-backoffice-ui/src/hooks/backend.ts b/packages/merchant-backoffice-ui/src/hooks/backend.ts
index 4305a9309..37dfd8fd6 100644
--- a/packages/merchant-backoffice-ui/src/hooks/backend.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/backend.ts
@@ -19,8 +19,13 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { AbsoluteTime, HttpStatusCode } from "@gnu-taler/taler-util";
import {
+ AbsoluteTime,
+ AccessToken,
+ HttpStatusCode,
+} from "@gnu-taler/taler-util";
+import {
+ EmptyObject,
ErrorType,
HttpError,
HttpResponse,
@@ -31,10 +36,8 @@ import {
} 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 { AccessToken, LoginToken, MerchantBackend, Timestamp } from "../declaration.js";
-
+import { useSessionContext } from "../context/session.js";
+import { LoginToken, MerchantBackend, Timestamp } from "../declaration.js";
export function useMatchMutate(): (
re?: RegExp,
@@ -49,18 +52,22 @@ export function useMatchMutate(): (
}
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,
- });
+ 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,
+ },
+ );
};
}
@@ -97,30 +104,36 @@ export function useBackendConfig(): HttpResponse<
const { request } = useBackendBaseRequest();
type Type = MerchantBackend.VersionResponse;
- type State = { data: HttpResponse<Type, RequestError<MerchantBackend.ErrorDetail>>, timer: number }
- const [result, setResult] = useState<State>({ data: { loading: true }, timer: 0 });
+ type State = {
+ data: HttpResponse<Type, RequestError<MerchantBackend.ErrorDetail>>;
+ timer: number;
+ };
+ const [result, setResult] = useState<State>({
+ data: { loading: true },
+ timer: 0,
+ });
useEffect(() => {
if (result.timer) {
- clearTimeout(result.timer)
+ clearTimeout(result.timer);
}
function tryConfig(): void {
request<Type>(`/config`)
.then((data) => {
const timer: any = setTimeout(() => {
- tryConfig()
- }, CHECK_CONFIG_INTERVAL_OK)
- setResult({ data, timer })
+ 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();
+ }, CHECK_CONFIG_INTERVAL_FAIL);
+ const data = error.cause;
+ setResult({ data, timer });
});
}
- tryConfig()
+ tryConfig();
}, [request]);
return result.data;
@@ -134,29 +147,29 @@ interface useBackendInstanceRequestType {
fetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
multiFetcher: <T>(params: [url: string[]]) => Promise<HttpResponseOk<T>[]>;
orderFetcher: <T>(
- params: [endpoint: string,
+ params: [
+ endpoint: string,
paid?: YesOrNo,
refunded?: YesOrNo,
wired?: YesOrNo,
searchDate?: Date,
- delta?: number,]
+ delta?: number,
+ ],
) => Promise<HttpResponseOk<T>>;
transferFetcher: <T>(
- params: [endpoint: string,
+ params: [
+ endpoint: string,
payto_uri?: string,
verified?: string,
position?: string,
- delta?: number,]
+ delta?: number,
+ ],
) => Promise<HttpResponseOk<T>>;
templateFetcher: <T>(
- params: [endpoint: string,
- position?: string,
- delta?: number]
+ params: [endpoint: string, position?: string, delta?: number],
) => Promise<HttpResponseOk<T>>;
webhookFetcher: <T>(
- params: [endpoint: string,
- position?: string,
- delta?: number]
+ params: [endpoint: string, position?: string, delta?: number],
) => Promise<HttpResponseOk<T>>;
}
interface useBackendBaseRequestType {
@@ -167,14 +180,16 @@ interface useBackendBaseRequestType {
}
type YesOrNo = "yes" | "no";
-type LoginResult = {
- valid: true;
- token: string;
- expiration: Timestamp;
-} | {
- valid: false;
- cause: HttpError<{}>;
-}
+type LoginResult =
+ | {
+ valid: true;
+ token: string;
+ expiration: Timestamp;
+ }
+ | {
+ valid: false;
+ cause: HttpError<EmptyObject>;
+ };
export function useCredentialsChecker() {
const { request } = useApiContext();
@@ -187,24 +202,34 @@ export function useCredentialsChecker() {
const data: MerchantBackend.Instances.LoginTokenRequest = {
scope: "write",
duration: {
- d_us: "forever"
+ d_us: "forever",
},
refreshable: true,
- }
+ };
try {
- const response = await request<MerchantBackend.Instances.LoginTokenSuccessResponse>(baseUrl, `/private/token`, {
- method: "POST",
- token,
- data
- });
- return { valid: true, token: response.data.token, expiration: response.data.expiration };
+ const response =
+ await request<MerchantBackend.Instances.LoginTokenSuccessResponse>(
+ baseUrl,
+ `/private/token`,
+ {
+ method: "POST",
+ token,
+ data,
+ },
+ );
+ return {
+ valid: true,
+ token: response.data.token,
+ expiration: response.data.expiration,
+ };
} catch (error) {
if (error instanceof RequestError) {
return { valid: false, cause: error.cause };
}
return {
- valid: false, cause: {
+ valid: false,
+ cause: {
type: ErrorType.UNEXPECTED,
loading: false,
info: {
@@ -212,23 +237,28 @@ export function useCredentialsChecker() {
status: 0,
options: {},
url: `/private/token`,
- payload: {}
+ payload: {},
},
exception: error,
- message: (error instanceof Error ? error.message : "unpexepected error")
- }
+ message:
+ error instanceof Error ? error.message : "unpexepected error",
+ },
};
}
- };
+ }
async function refreshLoginToken(
baseUrl: string,
- token: LoginToken
+ token: LoginToken,
): Promise<LoginResult> {
-
- if (AbsoluteTime.isExpired(AbsoluteTime.fromProtocolTimestamp(token.expiration))) {
+ if (
+ AbsoluteTime.isExpired(
+ AbsoluteTime.fromProtocolTimestamp(token.expiration),
+ )
+ ) {
return {
- valid: false, cause: {
+ valid: false,
+ cause: {
type: ErrorType.CLIENT,
status: HttpStatusCode.Unauthorized,
message: "login token expired, login again.",
@@ -237,16 +267,16 @@ export function useCredentialsChecker() {
status: 401,
options: {},
url: `/private/token`,
- payload: {}
+ payload: {},
},
- payload: {}
+ payload: {},
},
- }
+ };
}
- return requestNewLoginToken(baseUrl, token.token as AccessToken)
+ return requestNewLoginToken(baseUrl, token.token as AccessToken);
}
- return { requestNewLoginToken, refreshLoginToken }
+ return { requestNewLoginToken, refreshLoginToken };
}
/**
@@ -255,37 +285,36 @@ export function useCredentialsChecker() {
* @returns request handler to
*/
export function useBackendBaseRequest(): useBackendBaseRequestType {
- const { url: backend, token: loginToken } = useBackendContext();
const { request: requestHandler } = useApiContext();
- const token = loginToken?.token;
+ const { state } = useSessionContext();
+ const token = state.status === "loggedIn" ? state.token : undefined;
+ const baseUrl = state.backendUrl;
const request = useCallback(
function requestImpl<T>(
endpoint: string,
options: RequestOptions = {},
): Promise<HttpResponseOk<T>> {
- return requestHandler<T>(backend, endpoint, { ...options, token }).then(res => {
- return res
- }).catch(err => {
- throw err
- });
+ return requestHandler<T>(baseUrl, endpoint, { ...options, token })
+ .then((res) => {
+ return res;
+ })
+ .catch((err) => {
+ throw err;
+ });
},
- [backend, token],
+ [baseUrl, token],
);
return { request };
}
export function useBackendInstanceRequest(): useBackendInstanceRequestType {
- const { url: rootBackendUrl, token: rootToken } = useBackendContext();
- const { token: instanceToken, admin } = useInstanceContext();
const { request: requestHandler } = useApiContext();
- const { baseUrl, token: loginToken } = !admin
- ? { baseUrl: rootBackendUrl, token: rootToken }
- : { baseUrl: rootBackendUrl, token: instanceToken };
-
- const token = loginToken?.token;
+ const { state } = useSessionContext();
+ const token = state.status === "loggedIn" ? state.token : undefined;
+ const baseUrl = state.backendUrl;
const request = useCallback(
function requestImpl<T>(
@@ -301,7 +330,7 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
function multiFetcherImpl<T>(
args: [endpoints: string[]],
): Promise<HttpResponseOk<T>[]> {
- const [endpoints] = args
+ const [endpoints] = args;
return Promise.all(
endpoints.map((endpoint) =>
requestHandler<T>(baseUrl, endpoint, { token }),
@@ -320,18 +349,22 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
const orderFetcher = useCallback(
function orderFetcherImpl<T>(
- args: [endpoint: string,
+ args: [
+ endpoint: string,
paid?: YesOrNo,
refunded?: YesOrNo,
wired?: YesOrNo,
searchDate?: Date,
- delta?: number,]
+ delta?: number,
+ ],
): Promise<HttpResponseOk<T>> {
- const [endpoint, paid, refunded, wired, searchDate, delta] = args
+ 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;
+ : searchDate !== undefined
+ ? Math.floor(searchDate.getTime() / 1000)
+ : undefined;
const params: any = {};
if (paid !== undefined) params.paid = paid;
if (delta !== undefined) params.delta = delta;
@@ -339,12 +372,12 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
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
+ //in this case we can already assume the response
//and avoid network
return Promise.resolve({
ok: true,
data: { orders: [] } as T,
- })
+ });
}
return requestHandler<T>(baseUrl, endpoint, { params, token });
},
@@ -353,23 +386,25 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
const transferFetcher = useCallback(
function transferFetcherImpl<T>(
- args: [endpoint: string,
+ args: [
+ endpoint: string,
payto_uri?: string,
verified?: string,
position?: string,
- delta?: number,]
+ delta?: number,
+ ],
): Promise<HttpResponseOk<T>> {
- const [endpoint, payto_uri, verified, position, delta] = args
+ 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
+ //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;
@@ -383,19 +418,17 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
const templateFetcher = useCallback(
function templateFetcherImpl<T>(
- args: [endpoint: string,
- position?: string,
- delta?: number,]
+ args: [endpoint: string, position?: string, delta?: number],
): Promise<HttpResponseOk<T>> {
- const [endpoint, position, delta] = args
+ const [endpoint, position, delta] = args;
const params: any = {};
if (delta === 0) {
- //in this case we can already assume the response
+ //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;
@@ -409,19 +442,17 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
const webhookFetcher = useCallback(
function webhookFetcherImpl<T>(
- args: [endpoint: string,
- position?: string,
- delta?: number,]
+ args: [endpoint: string, position?: string, delta?: number],
): Promise<HttpResponseOk<T>> {
- const [endpoint, position, delta] = args
+ const [endpoint, position, delta] = args;
const params: any = {};
if (delta === 0) {
- //in this case we can already assume the response
+ //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;
diff --git a/packages/merchant-backoffice-ui/src/hooks/instance.test.ts b/packages/merchant-backoffice-ui/src/hooks/instance.test.ts
index 4f6cabc9e..a1bb3d5d4 100644
--- a/packages/merchant-backoffice-ui/src/hooks/instance.test.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/instance.test.ts
@@ -21,7 +21,7 @@
import * as tests from "@gnu-taler/web-util/testing";
import { expect } from "chai";
-import { AccessToken, MerchantBackend } from "../declaration.js";
+import { MerchantBackend } from "../declaration.js";
import {
useAdminAPI,
useBackendInstances,
@@ -40,6 +40,7 @@ import {
API_UPDATE_CURRENT_INSTANCE_AUTH,
API_UPDATE_INSTANCE_BY_ID,
} from "./urls.js";
+import { AccessToken } from "@gnu-taler/taler-util";
describe("instance api interaction with details", () => {
it("should evict cache when updating an instance", async () => {
diff --git a/packages/merchant-backoffice-ui/src/hooks/instance.ts b/packages/merchant-backoffice-ui/src/hooks/instance.ts
index 352f54982..dfe97fd61 100644
--- a/packages/merchant-backoffice-ui/src/hooks/instance.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/instance.ts
@@ -17,9 +17,9 @@ import {
HttpResponse,
HttpResponseOk,
RequestError,
+ useMerchantApiContext,
} from "@gnu-taler/web-util/browser";
-import { useBackendContext } from "../context/backend.js";
-import { AccessToken, MerchantBackend } from "../declaration.js";
+import { MerchantBackend } from "../declaration.js";
import {
useBackendBaseRequest,
useBackendInstanceRequest,
@@ -29,6 +29,8 @@ import {
// FIX default import https://github.com/microsoft/TypeScript/issues/49189
import _useSWR, { SWRHook, useSWRConfig } from "swr";
+import { useSessionContext } from "../context/session.js";
+import { AccessToken } from "@gnu-taler/taler-util";
const useSWR = _useSWR as unknown as SWRHook;
interface InstanceAPI {
@@ -37,7 +39,10 @@ interface InstanceAPI {
) => Promise<void>;
deleteInstance: () => Promise<void>;
clearAccessToken: (currentToken: AccessToken | undefined) => Promise<void>;
- setNewAccessToken: (currentToken: AccessToken | undefined, token: AccessToken) => Promise<void>;
+ setNewAccessToken: (
+ currentToken: AccessToken | undefined,
+ token: AccessToken,
+ ) => Promise<void>;
}
export function useAdminAPI(): AdminAPI {
@@ -87,10 +92,13 @@ export interface AdminAPI {
export function useManagementAPI(instanceId: string): InstanceAPI {
const mutateAll = useMatchMutate();
- const { url: backendURL } = useBackendContext()
- const { updateToken } = useBackendContext();
+ const {
+ state: { backendUrl },
+ logIn,
+ logOut,
+ } = useSessionContext();
const { request } = useBackendBaseRequest();
- const { requestNewLoginToken } = useCredentialsChecker()
+ const { requestNewLoginToken } = useCredentialsChecker();
const updateInstance = async (
instance: MerchantBackend.Instances.InstanceReconfigurationMessage,
@@ -111,7 +119,9 @@ export function useManagementAPI(instanceId: string): InstanceAPI {
mutateAll(/\/management\/instances/);
};
- const clearAccessToken = async (currentToken: AccessToken | undefined): Promise<void> => {
+ const clearAccessToken = async (
+ currentToken: AccessToken | undefined,
+ ): Promise<void> => {
await request(`/management/instances/${instanceId}/auth`, {
method: "POST",
token: currentToken,
@@ -121,36 +131,46 @@ export function useManagementAPI(instanceId: string): InstanceAPI {
mutateAll(/\/management\/instances/);
};
- const setNewAccessToken = async (currentToken: AccessToken | undefined, newToken: AccessToken): Promise<void> => {
+ const setNewAccessToken = async (
+ currentToken: AccessToken | undefined,
+ newToken: AccessToken,
+ ): Promise<void> => {
await request(`/management/instances/${instanceId}/auth`, {
method: "POST",
token: currentToken,
data: { method: "token", token: newToken },
});
- const resp = await requestNewLoginToken(backendURL, newToken)
+ const resp = await requestNewLoginToken(backendUrl, newToken);
if (resp.valid) {
- const { token, expiration } = resp
- updateToken({ token, expiration });
+ logIn({ token: resp.token as AccessToken });
} else {
- updateToken(undefined)
+ logOut();
}
mutateAll(/\/management\/instances/);
};
- return { updateInstance, deleteInstance, setNewAccessToken, clearAccessToken };
+ return {
+ updateInstance,
+ deleteInstance,
+ setNewAccessToken,
+ clearAccessToken,
+ };
}
export function useInstanceAPI(): InstanceAPI {
const { mutate } = useSWRConfig();
- const { url: backendURL, updateToken } = useBackendContext()
-
const {
- token: adminToken,
- } = useBackendContext();
+ state: { backendUrl },
+ } = useSessionContext();
+
const { request } = useBackendInstanceRequest();
- const { requestNewLoginToken } = useCredentialsChecker()
+ const { requestNewLoginToken } = useCredentialsChecker();
+ const { state, logIn, logOut } = useSessionContext();
+
+ const adminToken =
+ state.status === "loggedIn" && state.isAdmin ? state.token : undefined;
const updateInstance = async (
instance: MerchantBackend.Instances.InstanceReconfigurationMessage,
@@ -160,7 +180,9 @@ export function useInstanceAPI(): InstanceAPI {
data: instance,
});
- if (adminToken) mutate(["/private/instances", adminToken, backendURL], null);
+ if (adminToken) {
+ mutate(["/private/instances", adminToken, backendUrl], null);
+ }
mutate([`/private/`], null);
};
@@ -170,11 +192,15 @@ export function useInstanceAPI(): InstanceAPI {
// token: adminToken,
});
- if (adminToken) mutate(["/private/instances", adminToken, backendURL], null);
+ if (adminToken) {
+ mutate(["/private/instances", adminToken, backendUrl], null);
+ }
mutate([`/private/`], null);
};
- const clearAccessToken = async (currentToken: AccessToken | undefined): Promise<void> => {
+ const clearAccessToken = async (
+ currentToken: AccessToken | undefined,
+ ): Promise<void> => {
await request(`/private/auth`, {
method: "POST",
token: currentToken,
@@ -184,25 +210,32 @@ export function useInstanceAPI(): InstanceAPI {
mutate([`/private/`], null);
};
- const setNewAccessToken = async (currentToken: AccessToken | undefined, newToken: AccessToken): Promise<void> => {
+ const setNewAccessToken = async (
+ currentToken: AccessToken | undefined,
+ newToken: AccessToken,
+ ): Promise<void> => {
await request(`/private/auth`, {
method: "POST",
token: currentToken,
data: { method: "token", token: newToken },
});
- const resp = await requestNewLoginToken(backendURL, newToken)
+ const resp = await requestNewLoginToken(backendUrl, newToken);
if (resp.valid) {
- const { token, expiration } = resp
- updateToken({ token, expiration });
+ logIn({ token: resp.token as AccessToken });
} else {
- updateToken(undefined)
+ logOut();
}
mutate([`/private/`], null);
};
- return { updateInstance, deleteInstance, setNewAccessToken, clearAccessToken };
+ return {
+ updateInstance,
+ deleteInstance,
+ setNewAccessToken,
+ clearAccessToken,
+ };
}
export function useInstanceDetails(): HttpResponse<
diff --git a/packages/merchant-backoffice-ui/src/hooks/preference.ts b/packages/merchant-backoffice-ui/src/hooks/preference.ts
index 4570ff679..5a50eb378 100644
--- a/packages/merchant-backoffice-ui/src/hooks/preference.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/preference.ts
@@ -59,6 +59,7 @@ const PREFERENCES_KEY = buildStorageKey(
export function usePreference(): [
Readonly<Preferences>,
<T extends keyof Preferences>(key: T, value: Preferences[T]) => void,
+ (s: Preferences) => void,
] {
const { value, update } = useLocalStorage(PREFERENCES_KEY, defaultSettings);
function updateField<T extends keyof Preferences>(k: T, v: Preferences[T]) {
@@ -66,7 +67,7 @@ export function usePreference(): [
update(newValue);
}
- return [value, updateField];
+ return [value, updateField, update];
}
export function dateFormatForSettings(s: Preferences): string {
diff --git a/packages/merchant-backoffice-ui/src/hooks/session.ts b/packages/merchant-backoffice-ui/src/hooks/session.ts
deleted file mode 100644
index 8bf075e94..000000000
--- a/packages/merchant-backoffice-ui/src/hooks/session.ts
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- 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 <http://www.gnu.org/licenses/>
- */
-
-import {
- AccessToken,
- Codec,
- buildCodecForObject,
- buildCodecForUnion,
- codecForBoolean,
- codecForConstString,
- codecForConstTrue,
- codecForString,
- codecOptional,
-} from "@gnu-taler/taler-util";
-import { buildStorageKey, useLocalStorage, useMerchantApiContext } from "@gnu-taler/web-util/browser";
-import { mutate } from "swr";
-
-/**
- * Has the information to reach and
- * authenticate at the bank's backend.
- */
-export type SessionState = LoggedIn | LoggedOut | Expired | Impersonate;
-
-interface LoggedIn {
- status: "loggedIn";
- instance: string;
- isAdmin: boolean;
- token: AccessToken | undefined;
-}
-interface Expired {
- status: "expired";
- instance: string;
- isAdmin: boolean;
-}
-interface Impersonate {
- status: "impersonate";
- instance: string;
- isAdmin: true;
- token: AccessToken | undefined;
- originalInstance: string;
- originalToken: AccessToken | undefined;
-}
-interface LoggedOut {
- status: "loggedOut";
- instance: string;
- isAdmin: boolean;
-}
-
-export const codecForSessionStateLoggedIn = (): Codec<LoggedIn> =>
- buildCodecForObject<LoggedIn>()
- .property("status", codecForConstString("loggedIn"))
- .property("instance", codecForString())
- .property("token", codecOptional(codecForString() as Codec<AccessToken>))
- .property("isAdmin", codecForBoolean())
- .build("SessionState.LoggedIn");
-
-export const codecForSessionStateExpired = (): Codec<Expired> =>
- buildCodecForObject<Expired>()
- .property("status", codecForConstString("expired"))
- .property("instance", codecForString())
- .property("isAdmin", codecForBoolean())
- .build("SessionState.Expired");
-
-export const codecForSessionStateLoggedOut = (): Codec<LoggedOut> =>
- buildCodecForObject<LoggedOut>()
- .property("status", codecForConstString("loggedOut"))
- .property("instance", codecForString())
- .property("isAdmin", codecForBoolean())
- .build("SessionState.LoggedOut");
-
-export const codecForSessionStateImpresonate = (): Codec<Impersonate> =>
- buildCodecForObject<Impersonate>()
- .property("status", codecForConstString("impersonate"))
- .property("instance", codecForString())
- .property("isAdmin", codecForConstTrue())
- .property("token", codecOptional(codecForString() as Codec<AccessToken>))
- .property("originalInstance", codecForString())
- .property("originalToken", codecOptional(codecForString() as Codec<AccessToken>))
- .build("SessionState.Impersonate");
-
-export const codecForSessionState = (): Codec<SessionState> =>
- buildCodecForUnion<SessionState>()
- .discriminateOn("status")
- .alternative("loggedIn", codecForSessionStateLoggedIn())
- .alternative("impersonate", codecForSessionStateImpresonate())
- .alternative("loggedOut", codecForSessionStateLoggedOut())
- .alternative("expired", codecForSessionStateExpired())
- .build("SessionState");
-
-export const defaultState = (instance: string): SessionState => ({
- status: "loggedIn",
- instance,
- isAdmin: instance === DEFAULT_ADMIN_USERNAME,
- token: undefined,
-});
-
-export interface SessionStateHandler {
- state: SessionState;
- logOut(): void;
- expired(): void;
- logIn(info: { instance: string; token?: AccessToken }): void;
- impersonate(info: { instance: string; token?: AccessToken }): void;
-}
-
-const SESSION_STATE_KEY = buildStorageKey("merchant-session", codecForSessionState());
-
-export const DEFAULT_ADMIN_USERNAME = "default"
-
-export const INSTANCE_ID_LOOKUP = /\/instances\/([^/]*)\/?$/;
-
-/**
- * Return getters and setters for
- * login credentials and backend's
- * base URL.
- */
-export function useSessionState(): SessionStateHandler {
- const { url } = useMerchantApiContext();
-
- const match = INSTANCE_ID_LOOKUP.exec(url.href);
- const instanceName = !match || !match[1] ? DEFAULT_ADMIN_USERNAME : match[1];
-
- const { value: state, update } = useLocalStorage(
- SESSION_STATE_KEY,
- defaultState(instanceName),
- );
-
- return {
- state,
- logOut() {
- update(defaultState(instanceName));
- },
- impersonate(info) {
- if (state.status === "loggedOut" || state.status === "expired") {
- // can't impersonate if not loggedin
- return;
- }
- const nextState: SessionState = {
- status: "impersonate",
- originalToken: state.token,
- originalInstance: state.instance,
- isAdmin: true,
- instance: info.instance,
- token: info.token,
- };
- update(nextState);
- },
- expired() {
- if (state.status === "loggedOut") return;
- const nextState: SessionState = {
- status: "expired",
- instance: state.instance,
- isAdmin: state.instance === DEFAULT_ADMIN_USERNAME,
- };
- update(nextState);
- },
- logIn(info) {
- // admin is defined by the username
- const nextState: SessionState = {
- status: "loggedIn",
- instance: info.instance,
- token: info.token,
- isAdmin: state.instance === DEFAULT_ADMIN_USERNAME,
- };
- update(nextState);
- cleanAllCache();
- },
- };
-}
-
-function cleanAllCache(): void {
- mutate(() => true, undefined, { revalidate: false });
-}
diff --git a/packages/merchant-backoffice-ui/src/hooks/testing.tsx b/packages/merchant-backoffice-ui/src/hooks/testing.tsx
index d9a70e794..bebf7716b 100644
--- a/packages/merchant-backoffice-ui/src/hooks/testing.tsx
+++ b/packages/merchant-backoffice-ui/src/hooks/testing.tsx
@@ -24,8 +24,6 @@ import { ComponentChildren, FunctionalComponent, h, VNode } from "preact";
import { HttpRequestLibrary, HttpRequestOptions, HttpResponse } from "@gnu-taler/taler-util/http";
import { SWRConfig } from "swr";
import { ApiContextProvider } from "@gnu-taler/web-util/browser";
-import { BackendContextProvider } from "../context/backend.js";
-import { InstanceContextProvider } from "../context/instance.js";
import { HttpResponseOk, RequestOptions } from "@gnu-taler/web-util/browser";
import { TalerBankIntegrationHttpClient, TalerCoreBankHttpClient, TalerRevenueHttpClient, TalerWireGatewayHttpClient } from "@gnu-taler/taler-util";
@@ -149,15 +147,15 @@ export class ApiMockEnvironment extends MockEnvironment {
const bankWire = new TalerWireGatewayHttpClient(bankCore.getWireGatewayAPI("b").href, "b", mockHttpClient)
return (
- <BackendContextProvider defaultUrl="http://backend">
- <InstanceContextProvider
- value={{
- token: undefined,
- id: "default",
- admin: true,
- changeToken: () => null,
- }}
- >
+ // <BackendContextProvider defaultUrl="http://backend">
+ // <InstanceContextProvider
+ // value={{
+ // token: undefined,
+ // id: "default",
+ // admin: true,
+ // changeToken: () => null,
+ // }}
+ // >
<ApiContextProvider value={{ request, bankCore, bankIntegration, bankRevenue, bankWire }}>
<SC
value={{
@@ -172,8 +170,8 @@ export class ApiMockEnvironment extends MockEnvironment {
{children}
</SC>
</ApiContextProvider>
- </InstanceContextProvider>
- </BackendContextProvider>
+ // </InstanceContextProvider>
+ // </BackendContextProvider>
);
};
}