UNPKG

@toruslabs/http-helpers

Version:
303 lines (299 loc) 10.9 kB
'use strict'; var _objectSpread = require('@babel/runtime/helpers/objectSpread2'); var merge = require('deepmerge'); var logLevel = require('loglevel'); const log = logLevel.getLogger("http-helpers"); log.setLevel(logLevel.levels.INFO); let apiKey = "torus-default"; let embedHost = ""; // #region API Keys const gatewayAuthHeader = "x-api-key"; const gatewayEmbedHostHeader = "x-embed-host"; let sentry = null; const tracingOrigins = []; const tracingPaths = []; function enableSentryTracing(_sentry, _tracingOrigins, _tracingPaths) { sentry = _sentry; tracingOrigins.push(..._tracingOrigins); tracingPaths.push(..._tracingPaths); } function setEmbedHost(embedHost_) { embedHost = embedHost_; } function clearEmbedHost() { embedHost = ""; } function getEmbedHost() { return embedHost; } function setAPIKey(apiKey_) { apiKey = apiKey_; } function clearAPIKey() { apiKey = "torus-default"; } function getAPIKey() { return apiKey; } // #endregion function setLogLevel(level) { log.setLevel(level); } async function fetchAndTrace(url, init) { let _url = null; try { _url = new URL(url); } catch { // ignore } if (sentry && _url && (tracingOrigins.includes(_url.origin) || tracingPaths.includes(_url.pathname))) { const result = await sentry.startSpan({ name: url, op: "http.client" }, async () => { const response = await fetch(url, init); return response; }); return result; } return fetch(url, init); } function getApiKeyHeaders() { const headers = {}; if (apiKey) headers[gatewayAuthHeader] = apiKey; if (embedHost) headers[gatewayEmbedHostHeader] = embedHost; return headers; } function debugLogResponse(response) { log.info(`Response: ${response.status} ${response.statusText}`); log.info(`Url: ${response.url}`); } function logTracingHeader(response) { const tracingHeader = response.headers.get("x-web3-correlation-id"); if (tracingHeader) log.info(`Request tracing with traceID = ${tracingHeader}`); } const promiseTimeout = async (ms, promise) => { let timeoutFunc = null; try { const timeout = new Promise((_resolve, reject) => { timeoutFunc = setTimeout(() => { reject(new Error(`Timed out in ${ms}ms`)); }, ms); }); const result = await Promise.race([promise, timeout]); // promise.race will return the first resolved promise // then we clear the timeout if (timeoutFunc != null) { clearTimeout(timeoutFunc); } return result; } catch (err) { // clear the timeout if (timeoutFunc != null) { clearTimeout(timeoutFunc); } // rethrow the original error throw err; } }; const get = async (url, options_ = {}, customOptions = {}) => { const defaultOptions = { mode: "cors", headers: {} }; if (customOptions.useAPIKey) { defaultOptions.headers = _objectSpread(_objectSpread({}, defaultOptions.headers), getApiKeyHeaders()); } options_.method = "GET"; const options = merge(defaultOptions, options_); const response = await fetchAndTrace(url, options); if (response.ok) { const responseContentType = response.headers.get("content-type"); if (responseContentType !== null && responseContentType !== void 0 && responseContentType.includes("application/json")) { return response.json(); } return response.text(); } debugLogResponse(response); throw response; }; const post = (url, data = {}, options_ = {}, customOptions = {}) => { const defaultOptions = { mode: "cors", headers: { "Content-Type": "application/json; charset=utf-8" } }; if (customOptions.useAPIKey) { defaultOptions.headers = _objectSpread(_objectSpread({}, defaultOptions.headers), getApiKeyHeaders()); } options_.method = "POST"; const options = merge(defaultOptions, options_); // deep merge changes the structure of form data and url encoded data , // so we should not deepmerge body data if (customOptions.isUrlEncodedData) { // for multipart request browser/client will add multipart content type // along with multipart boundary , so for multipart request send // content-type: undefined or send with multipart boundary if already known options.body = data; // If url encoded data, this must not be the content type if (options.headers["Content-Type"] === "application/json; charset=utf-8") delete options.headers["Content-Type"]; } else { options.body = JSON.stringify(data); } return promiseTimeout(customOptions.timeout || 60000, fetchAndTrace(url, options).then(response => { if (customOptions.logTracingHeader) { logTracingHeader(response); } if (response.ok) { const responseContentType = response.headers.get("content-type"); if (responseContentType !== null && responseContentType !== void 0 && responseContentType.includes("application/json")) { return response.json(); } return response.text(); } debugLogResponse(response); throw response; })); }; const patch = async (url, data = {}, options_ = {}, customOptions = {}) => { const defaultOptions = { mode: "cors", headers: { "Content-Type": "application/json; charset=utf-8" } }; // for multipart request browser/client will add multipart content type // along with multipart boundary , so for multipart request send // content-type: undefined or send with multipart boundary if already known if (customOptions.useAPIKey) { defaultOptions.headers = _objectSpread(_objectSpread({}, defaultOptions.headers), getApiKeyHeaders()); } options_.method = "PATCH"; const options = merge(defaultOptions, options_); // deep merge changes the structure of form data and url encoded data , // so we should not deepmerge body data if (customOptions.isUrlEncodedData) { // for multipart request browser/client will add multipart content type // along with multipart boundary , so for multipart request send // content-type: undefined or send with multipart boundary if already known options.body = data; // If url encoded data, this must not be the content type if (options.headers["Content-Type"] === "application/json; charset=utf-8") delete options.headers["Content-Type"]; } else { options.body = JSON.stringify(data); } const response = await fetchAndTrace(url, options); if (response.ok) { const responseContentType = response.headers.get("content-type"); if (responseContentType !== null && responseContentType !== void 0 && responseContentType.includes("application/json")) { return response.json(); } return response.text(); } debugLogResponse(response); throw response; }; const put = async (url, data = {}, options_ = {}, customOptions = {}) => { const defaultOptions = { mode: "cors", headers: { "Content-Type": "application/json; charset=utf-8" } }; // for multipart request browser/client will add multipart content type // along with multipart boundary , so for multipart request send // content-type: undefined or send with multipart boundary if already known if (customOptions.useAPIKey) { defaultOptions.headers = _objectSpread(_objectSpread({}, defaultOptions.headers), getApiKeyHeaders()); } options_.method = "PUT"; const options = merge(defaultOptions, options_); // deep merge changes the structure of form data and url encoded data , // so we should not deepmerge body data if (customOptions.isUrlEncodedData) { // for multipart request browser/client will add multipart content type // along with multipart boundary , so for multipart request send // content-type: undefined or send with multipart boundary if already known options.body = data; // If url encoded data, this must not be the content type if (options.headers["Content-Type"] === "application/json; charset=utf-8") delete options.headers["Content-Type"]; } else { options.body = JSON.stringify(data); } const response = await fetchAndTrace(url, options); if (response.ok) { const responseContentType = response.headers.get("content-type"); if (responseContentType !== null && responseContentType !== void 0 && responseContentType.includes("application/json")) { return response.json(); } return response.text(); } debugLogResponse(response); throw response; }; const remove = async (url, data = {}, options_ = {}, customOptions = {}) => { const defaultOptions = { mode: "cors", headers: { "Content-Type": "application/json; charset=utf-8" } }; // for multipart request browser/client will add multipart content type // along with multipart boundary , so for multipart request send // content-type: undefined or send with multipart boundary if already known if (customOptions.useAPIKey) { defaultOptions.headers = _objectSpread(_objectSpread({}, defaultOptions.headers), getApiKeyHeaders()); } options_.method = "DELETE"; const options = merge(defaultOptions, options_); if (customOptions.isUrlEncodedData) { // for multipart request browser/client will add multipart content type // along with multipart boundary , so for multipart request send // content-type: undefined or send with multipart boundary if already known options.body = data; // If url encoded data, this must not be the content type if (options.headers["Content-Type"] === "application/json; charset=utf-8") delete options.headers["Content-Type"]; } else { options.body = JSON.stringify(data); } const response = await fetchAndTrace(url, options); if (response.ok) { const responseContentType = response.headers.get("content-type"); if (responseContentType !== null && responseContentType !== void 0 && responseContentType.includes("application/json")) { return response.json(); } return response.text(); } debugLogResponse(response); throw response; }; const generateJsonRPCObject = (method, parameters) => ({ jsonrpc: "2.0", method, id: 10, params: parameters }); const promiseRace = (url, options, timeout = 60000) => Promise.race([get(url, options), new Promise((_resolve, reject) => { setTimeout(() => { reject(new Error("timed out")); }, timeout); })]); exports.clearAPIKey = clearAPIKey; exports.clearEmbedHost = clearEmbedHost; exports.enableSentryTracing = enableSentryTracing; exports.gatewayAuthHeader = gatewayAuthHeader; exports.gatewayEmbedHostHeader = gatewayEmbedHostHeader; exports.generateJsonRPCObject = generateJsonRPCObject; exports.get = get; exports.getAPIKey = getAPIKey; exports.getEmbedHost = getEmbedHost; exports.patch = patch; exports.post = post; exports.promiseRace = promiseRace; exports.promiseTimeout = promiseTimeout; exports.put = put; exports.remove = remove; exports.setAPIKey = setAPIKey; exports.setEmbedHost = setEmbedHost; exports.setLogLevel = setLogLevel;