@graphql-hive/core
Version:
125 lines (124 loc) • 5.91 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.URL = exports.http = void 0;
exports.makeFetchCall = makeFetchCall;
const tslib_1 = require("tslib");
const async_retry_1 = tslib_1.__importDefault(require("async-retry"));
const fetch_1 = require("@whatwg-node/fetch");
Object.defineProperty(exports, "URL", { enumerable: true, get: function () { return fetch_1.URL; } });
function get(endpoint, config) {
return makeFetchCall(endpoint, {
method: 'GET',
headers: config.headers,
timeout: config.timeout,
retry: config.retry,
fetchImplementation: config.fetchImplementation,
logger: config.logger,
isRequestOk: config.isRequestOk,
});
}
function post(endpoint, data, config) {
return makeFetchCall(endpoint, Object.assign({ body: data, method: 'POST' }, config));
}
exports.http = {
get,
post,
};
async function makeFetchCall(endpoint, config) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
const logger = config.logger;
const isRequestOk = (_a = config.isRequestOk) !== null && _a !== void 0 ? _a : (response => response.ok);
let retries = 0;
let minTimeout = 200;
let maxTimeout = 2000;
let factor = 1.2;
if (config.retry !== false) {
retries = (_c = (_b = config.retry) === null || _b === void 0 ? void 0 : _b.retries) !== null && _c !== void 0 ? _c : 5;
minTimeout = (_e = (_d = config.retry) === null || _d === void 0 ? void 0 : _d.minTimeout) !== null && _e !== void 0 ? _e : 200;
maxTimeout = (_g = (_f = config.retry) === null || _f === void 0 ? void 0 : _f.maxTimeout) !== null && _g !== void 0 ? _g : 2000;
factor = (_j = (_h = config.retry) === null || _h === void 0 ? void 0 : _h.factor) !== null && _j !== void 0 ? _j : 1.2;
}
return await (0, async_retry_1.default)(async (bail, attempt) => {
var _a, _b;
const requestId = fetch_1.crypto.randomUUID();
logger === null || logger === void 0 ? void 0 : logger.info(`${config.method} ${endpoint} (x-request-id=${requestId})` +
(retries > 0 ? ' ' + getAttemptMessagePart(attempt, retries + 1) : ''));
const getDuration = measureTime();
const signal = AbortSignal.timeout((_a = config.timeout) !== null && _a !== void 0 ? _a : 20000);
const response = await ((_b = config.fetchImplementation) !== null && _b !== void 0 ? _b : fetch_1.fetch)(endpoint, {
method: config.method,
body: config.body,
headers: Object.assign({ 'x-request-id': requestId }, config.headers),
signal,
}).catch((error) => {
const logErrorMessage = () => logger === null || logger === void 0 ? void 0 : logger.error(`${config.method} ${endpoint} (x-request-id=${requestId}) failed ${getDuration()}. ` +
getErrorMessage(error));
if (isAggregateError(error)) {
for (const err of error.errors) {
logger === null || logger === void 0 ? void 0 : logger.error(err);
}
logErrorMessage();
throw new Error(`Unexpected HTTP error. (x-request-id=${requestId})`, { cause: error });
}
logger === null || logger === void 0 ? void 0 : logger.error(error);
logErrorMessage();
throw new Error(`Unexpected HTTP error. (x-request-id=${requestId})`, { cause: error });
});
if (isRequestOk(response)) {
logger === null || logger === void 0 ? void 0 : logger.info(`${config.method} ${endpoint} (x-request-id=${requestId}) succeeded with status ${response.status} ${getDuration()}.`);
return response;
}
logger === null || logger === void 0 ? void 0 : logger.error(`${config.method} ${endpoint} (x-request-id=${requestId}) failed with status ${response.status} ${getDuration()}: ${(await response.text()) || '<empty response body>'}`);
if (retries > 0 && attempt > retries) {
logger === null || logger === void 0 ? void 0 : logger.error(`${config.method} ${endpoint} (x-request-id=${requestId}) retry limit exceeded after ${attempt} attempts.`);
}
const error = new Error(`${config.method} ${endpoint} (x-request-id=${requestId}) failed with status ${response.status}.`);
if (response.status >= 400 && response.status < 500) {
if (retries > 0) {
logger === null || logger === void 0 ? void 0 : logger.error(`Abort retry because of status code ${response.status}.`);
}
bail(error);
}
throw error;
}, {
retries,
minTimeout,
maxTimeout,
factor,
});
}
function getErrorMessage(error) {
if (error && typeof error === 'object' && 'message' in error) {
return String(error.message);
}
return '<no error message>';
}
function getAttemptMessagePart(attempt, retry) {
return `Attempt (${attempt}/${retry})`;
}
function measureTime() {
const start = Date.now();
return () => '(' + formatTimestamp(Date.now() - start) + ')';
}
function formatTimestamp(timestamp) {
const milliseconds = timestamp % 1000;
const seconds = Math.floor((timestamp / 1000) % 60);
const minutes = Math.floor((timestamp / (1000 * 60)) % 60);
const hours = Math.floor(timestamp / (1000 * 60 * 60));
const parts = [];
if (hours > 0) {
parts.push(`${hours}h`);
}
if (minutes > 0 || hours > 0) {
// Include minutes if hours exist, even if minutes are 0
parts.push(`${minutes}m`);
}
if (seconds > 0 || minutes > 0 || hours > 0) {
parts.push(`${seconds}s`);
}
parts.push(`${milliseconds}ms`);
return parts.join(':');
}
function isAggregateError(error) {
return !!error && typeof error === 'object' && 'errors' in error && Array.isArray(error.errors);
}
;