moving libs to web utils, apply new mock api to backoffice
This commit is contained in:
parent
b9c24772f5
commit
c5c00e4da7
@ -71,16 +71,19 @@ export class ApiMockEnvironment extends MockEnvironment {
|
||||
request: options.data,
|
||||
},
|
||||
);
|
||||
const status = mocked.expectedQuery?.query.code ?? 200;
|
||||
const requestPayload = mocked.expectedQuery?.params?.request;
|
||||
const responsePayload = mocked.expectedQuery?.params?.response;
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
data: (!mocked ? undefined : mocked.payload) as T,
|
||||
data: responsePayload as T,
|
||||
loading: false,
|
||||
clientError: false,
|
||||
serverError: false,
|
||||
info: {
|
||||
hasToken: !!options.token,
|
||||
status: !mocked ? 200 : mocked.status,
|
||||
status,
|
||||
url: _url.href,
|
||||
payload: options.data,
|
||||
options: {},
|
||||
|
@ -1,203 +0,0 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2022 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/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import {
|
||||
Logger,
|
||||
RequestThrottler,
|
||||
TalerErrorCode,
|
||||
TalerError,
|
||||
} from "@gnu-taler/taler-util";
|
||||
|
||||
import {
|
||||
HttpRequestLibrary,
|
||||
HttpRequestOptions,
|
||||
HttpResponse,
|
||||
Headers,
|
||||
} from "@gnu-taler/taler-util/http";
|
||||
|
||||
const logger = new Logger("browserHttpLib");
|
||||
|
||||
/**
|
||||
* An implementation of the [[HttpRequestLibrary]] using the
|
||||
* browser's XMLHttpRequest.
|
||||
*/
|
||||
export class BrowserHttpLib implements HttpRequestLibrary {
|
||||
private throttle = new RequestThrottler();
|
||||
private throttlingEnabled = true;
|
||||
|
||||
fetch(
|
||||
requestUrl: string,
|
||||
options?: HttpRequestOptions,
|
||||
): Promise<HttpResponse> {
|
||||
const requestMethod = options?.method ?? "GET";
|
||||
const requestBody = options?.body;
|
||||
|
||||
if (this.throttlingEnabled && this.throttle.applyThrottle(requestUrl)) {
|
||||
const parsedUrl = new URL(requestUrl);
|
||||
throw TalerError.fromDetail(
|
||||
TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED,
|
||||
{
|
||||
requestMethod,
|
||||
requestUrl,
|
||||
throttleStats: this.throttle.getThrottleStats(requestUrl),
|
||||
},
|
||||
`request to origin ${parsedUrl.origin} was throttled`,
|
||||
);
|
||||
}
|
||||
|
||||
return new Promise<HttpResponse>((resolve, reject) => {
|
||||
const myRequest = new XMLHttpRequest();
|
||||
myRequest.open(requestMethod, requestUrl);
|
||||
if (options?.headers) {
|
||||
for (const headerName in options.headers) {
|
||||
myRequest.setRequestHeader(headerName, options.headers[headerName]);
|
||||
}
|
||||
}
|
||||
myRequest.responseType = "arraybuffer";
|
||||
if (requestBody) {
|
||||
if (requestBody instanceof ArrayBuffer) {
|
||||
myRequest.send(requestBody);
|
||||
} else if (ArrayBuffer.isView(requestBody)) {
|
||||
myRequest.send(requestBody);
|
||||
} else if (typeof requestBody === "string") {
|
||||
myRequest.send(requestBody);
|
||||
} else {
|
||||
myRequest.send(JSON.stringify(requestBody));
|
||||
}
|
||||
} else {
|
||||
myRequest.send();
|
||||
}
|
||||
|
||||
myRequest.onerror = (e) => {
|
||||
logger.error("http request error");
|
||||
reject(
|
||||
TalerError.fromDetail(
|
||||
TalerErrorCode.WALLET_NETWORK_ERROR,
|
||||
{
|
||||
requestUrl,
|
||||
requestMethod,
|
||||
},
|
||||
"Could not make request",
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
myRequest.addEventListener("readystatechange", (e) => {
|
||||
if (myRequest.readyState === XMLHttpRequest.DONE) {
|
||||
if (myRequest.status === 0) {
|
||||
const exc = TalerError.fromDetail(
|
||||
TalerErrorCode.WALLET_NETWORK_ERROR,
|
||||
{
|
||||
requestUrl,
|
||||
requestMethod,
|
||||
},
|
||||
"HTTP request failed (status 0, maybe URI scheme was wrong?)",
|
||||
);
|
||||
reject(exc);
|
||||
return;
|
||||
}
|
||||
const makeText = async (): Promise<string> => {
|
||||
const td = new TextDecoder();
|
||||
return td.decode(myRequest.response);
|
||||
};
|
||||
const makeJson = async (): Promise<any> => {
|
||||
let responseJson;
|
||||
try {
|
||||
const td = new TextDecoder();
|
||||
const responseString = td.decode(myRequest.response);
|
||||
responseJson = JSON.parse(responseString);
|
||||
} catch (e) {
|
||||
throw TalerError.fromDetail(
|
||||
TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
|
||||
{
|
||||
requestUrl,
|
||||
requestMethod,
|
||||
httpStatusCode: myRequest.status,
|
||||
},
|
||||
"Invalid JSON from HTTP response",
|
||||
);
|
||||
}
|
||||
if (responseJson === null || typeof responseJson !== "object") {
|
||||
throw TalerError.fromDetail(
|
||||
TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
|
||||
{
|
||||
requestUrl,
|
||||
requestMethod,
|
||||
httpStatusCode: myRequest.status,
|
||||
},
|
||||
"Invalid JSON from HTTP response",
|
||||
);
|
||||
}
|
||||
return responseJson;
|
||||
};
|
||||
|
||||
const headers = myRequest.getAllResponseHeaders();
|
||||
const arr = headers.trim().split(/[\r\n]+/);
|
||||
|
||||
// Create a map of header names to values
|
||||
const headerMap: Headers = new Headers();
|
||||
arr.forEach(function (line) {
|
||||
const parts = line.split(": ");
|
||||
const headerName = parts.shift();
|
||||
if (!headerName) {
|
||||
logger.warn("skipping invalid header");
|
||||
return;
|
||||
}
|
||||
const value = parts.join(": ");
|
||||
headerMap.set(headerName, value);
|
||||
});
|
||||
const resp: HttpResponse = {
|
||||
requestUrl: requestUrl,
|
||||
status: myRequest.status,
|
||||
headers: headerMap,
|
||||
requestMethod: requestMethod,
|
||||
json: makeJson,
|
||||
text: makeText,
|
||||
bytes: async () => myRequest.response,
|
||||
};
|
||||
resolve(resp);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
get(url: string, opt?: HttpRequestOptions): Promise<HttpResponse> {
|
||||
return this.fetch(url, {
|
||||
method: "GET",
|
||||
...opt,
|
||||
});
|
||||
}
|
||||
|
||||
postJson(
|
||||
url: string,
|
||||
body: any,
|
||||
opt?: HttpRequestOptions,
|
||||
): Promise<HttpResponse> {
|
||||
return this.fetch(url, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
...opt,
|
||||
});
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2022 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/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* API to access the Taler crypto worker thread.
|
||||
* @author Florian Dold
|
||||
*/
|
||||
|
||||
import {
|
||||
CryptoWorker,
|
||||
CryptoWorkerFactory,
|
||||
SynchronousCryptoWorkerPlain,
|
||||
} from "@gnu-taler/taler-wallet-core";
|
||||
|
||||
export class SynchronousCryptoWorkerFactory implements CryptoWorkerFactory {
|
||||
startWorker(): CryptoWorker {
|
||||
return new SynchronousCryptoWorkerPlain();
|
||||
}
|
||||
|
||||
getConcurrency(): number {
|
||||
return 1;
|
||||
}
|
||||
}
|
@ -1,205 +0,0 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2022 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/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import {
|
||||
RequestThrottler,
|
||||
TalerErrorCode,
|
||||
TalerError,
|
||||
} from "@gnu-taler/taler-util";
|
||||
|
||||
import {
|
||||
Headers,
|
||||
HttpRequestLibrary,
|
||||
HttpRequestOptions,
|
||||
HttpResponse,
|
||||
} from "@gnu-taler/taler-util/http";
|
||||
|
||||
/**
|
||||
* An implementation of the [[HttpRequestLibrary]] using the
|
||||
* browser's XMLHttpRequest.
|
||||
*/
|
||||
export class ServiceWorkerHttpLib implements HttpRequestLibrary {
|
||||
private throttle = new RequestThrottler();
|
||||
private throttlingEnabled = true;
|
||||
|
||||
async fetch(
|
||||
requestUrl: string,
|
||||
options?: HttpRequestOptions,
|
||||
): Promise<HttpResponse> {
|
||||
const requestMethod = options?.method ?? "GET";
|
||||
const requestBody = options?.body;
|
||||
const requestHeader = options?.headers;
|
||||
const requestTimeout = options?.timeout ?? { d_ms: 2 * 1000 };
|
||||
|
||||
if (this.throttlingEnabled && this.throttle.applyThrottle(requestUrl)) {
|
||||
const parsedUrl = new URL(requestUrl);
|
||||
throw TalerError.fromDetail(
|
||||
TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED,
|
||||
{
|
||||
requestMethod,
|
||||
requestUrl,
|
||||
throttleStats: this.throttle.getThrottleStats(requestUrl),
|
||||
},
|
||||
`request to origin ${parsedUrl.origin} was throttled`,
|
||||
);
|
||||
}
|
||||
|
||||
let myBody: BodyInit | undefined = undefined;
|
||||
if (requestBody != null) {
|
||||
if (typeof requestBody === "string") {
|
||||
myBody = requestBody;
|
||||
} else if (requestBody instanceof ArrayBuffer) {
|
||||
myBody = requestBody;
|
||||
} else if (ArrayBuffer.isView(requestBody)) {
|
||||
myBody = requestBody;
|
||||
} else if (typeof requestBody === "object") {
|
||||
myBody = JSON.stringify(requestBody);
|
||||
} else {
|
||||
throw Error("unsupported request body type");
|
||||
}
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
let timeoutId: any | undefined;
|
||||
if (requestTimeout.d_ms !== "forever") {
|
||||
timeoutId = setTimeout(() => {
|
||||
controller.abort(TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT);
|
||||
}, requestTimeout.d_ms);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(requestUrl, {
|
||||
headers: requestHeader,
|
||||
body: myBody,
|
||||
method: requestMethod,
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
|
||||
const headerMap = new Headers();
|
||||
response.headers.forEach((value, key) => {
|
||||
headerMap.set(key, value);
|
||||
});
|
||||
return {
|
||||
headers: headerMap,
|
||||
status: response.status,
|
||||
requestMethod,
|
||||
requestUrl,
|
||||
json: makeJsonHandler(response, requestUrl, requestMethod),
|
||||
text: makeTextHandler(response, requestUrl, requestMethod),
|
||||
bytes: async () => (await response.blob()).arrayBuffer(),
|
||||
};
|
||||
} catch (e) {
|
||||
if (controller.signal) {
|
||||
throw TalerError.fromDetail(
|
||||
controller.signal.reason,
|
||||
{},
|
||||
`request to ${requestUrl} timed out`,
|
||||
);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
get(url: string, opt?: HttpRequestOptions): Promise<HttpResponse> {
|
||||
return this.fetch(url, {
|
||||
method: "GET",
|
||||
...opt,
|
||||
});
|
||||
}
|
||||
|
||||
postJson(
|
||||
url: string,
|
||||
body: any,
|
||||
opt?: HttpRequestOptions,
|
||||
): Promise<HttpResponse> {
|
||||
return this.fetch(url, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
...opt,
|
||||
});
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
function makeTextHandler(
|
||||
response: Response,
|
||||
requestUrl: string,
|
||||
requestMethod: string,
|
||||
) {
|
||||
return async function getJsonFromResponse(): Promise<any> {
|
||||
let respText;
|
||||
try {
|
||||
respText = await response.text();
|
||||
} catch (e) {
|
||||
throw TalerError.fromDetail(
|
||||
TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
|
||||
{
|
||||
requestUrl,
|
||||
requestMethod,
|
||||
httpStatusCode: response.status,
|
||||
},
|
||||
"Invalid JSON from HTTP response",
|
||||
);
|
||||
}
|
||||
return respText;
|
||||
};
|
||||
}
|
||||
|
||||
function makeJsonHandler(
|
||||
response: Response,
|
||||
requestUrl: string,
|
||||
requestMethod: string,
|
||||
) {
|
||||
return async function getJsonFromResponse(): Promise<any> {
|
||||
let responseJson;
|
||||
try {
|
||||
responseJson = await response.json();
|
||||
} catch (e) {
|
||||
throw TalerError.fromDetail(
|
||||
TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
|
||||
{
|
||||
requestUrl,
|
||||
requestMethod,
|
||||
httpStatusCode: response.status,
|
||||
},
|
||||
"Invalid JSON from HTTP response",
|
||||
);
|
||||
}
|
||||
if (responseJson === null || typeof responseJson !== "object") {
|
||||
throw TalerError.fromDetail(
|
||||
TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
|
||||
{
|
||||
requestUrl,
|
||||
requestMethod,
|
||||
httpStatusCode: response.status,
|
||||
},
|
||||
"Invalid JSON from HTTP response",
|
||||
);
|
||||
}
|
||||
return responseJson;
|
||||
};
|
||||
}
|
@ -33,10 +33,15 @@ import {
|
||||
setGlobalLogLevelFromString,
|
||||
setLogLevelFromString,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import {
|
||||
ServiceWorkerHttpLib,
|
||||
BrowserHttpLib,
|
||||
} from "@gnu-taler/web-util/lib/index.browser";
|
||||
import {
|
||||
DbAccess,
|
||||
OpenedPromise,
|
||||
SetTimeoutTimerAPI,
|
||||
SynchronousCryptoWorkerFactoryPlain,
|
||||
Wallet,
|
||||
WalletOperations,
|
||||
WalletStoresV1,
|
||||
@ -46,15 +51,12 @@ import {
|
||||
openPromise,
|
||||
openTalerDatabase,
|
||||
} from "@gnu-taler/taler-wallet-core";
|
||||
import { BrowserHttpLib } from "./browserHttpLib.js";
|
||||
import {
|
||||
MessageFromBackend,
|
||||
MessageFromFrontend,
|
||||
MessageResponse,
|
||||
} from "./platform/api.js";
|
||||
import { platform } from "./platform/background.js";
|
||||
import { SynchronousCryptoWorkerFactory } from "./serviceWorkerCryptoWorkerFactory.js";
|
||||
import { ServiceWorkerHttpLib } from "./serviceWorkerHttpLib.js";
|
||||
import { ExtensionOperations } from "./taler-wallet-interaction-loader.js";
|
||||
import { BackgroundOperations } from "./wxApi.js";
|
||||
|
||||
@ -308,14 +310,14 @@ async function reinitWallet(): Promise<void> {
|
||||
|
||||
if (platform.useServiceWorkerAsBackgroundProcess()) {
|
||||
httpLib = new ServiceWorkerHttpLib();
|
||||
cryptoWorker = new SynchronousCryptoWorkerFactory();
|
||||
cryptoWorker = new SynchronousCryptoWorkerFactoryPlain();
|
||||
timer = new SetTimeoutTimerAPI();
|
||||
} else {
|
||||
httpLib = new BrowserHttpLib();
|
||||
// We could (should?) use the BrowserCryptoWorkerFactory here,
|
||||
// but right now we don't, to have less platform differences.
|
||||
// cryptoWorker = new BrowserCryptoWorkerFactory();
|
||||
cryptoWorker = new SynchronousCryptoWorkerFactory();
|
||||
cryptoWorker = new SynchronousCryptoWorkerFactoryPlain();
|
||||
timer = new SetTimeoutTimerAPI();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user