@graphql-hive/core
Version:
99 lines (98 loc) • 3.85 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createCDNArtifactFetcher = createCDNArtifactFetcher;
const tslib_1 = require("tslib");
const circuit_js_1 = tslib_1.__importDefault(require("../circuit-breaker/circuit.js"));
const version_js_1 = require("../version.js");
const circuit_breaker_js_1 = require("./circuit-breaker.js");
const http_client_js_1 = require("./http-client.js");
const utils_js_1 = require("./utils.js");
function isRequestOk(response) {
return response.status === 304 || response.ok;
}
/**
* Create a handler for fetching a CDN artifact with built-in cache and circuit breaker.
* It is intended for polling supergraph, schema sdl or services.
*/
function createCDNArtifactFetcher(args) {
var _a, _b;
const logger = (0, utils_js_1.chooseLogger)(args.logger);
let cacheETag = null;
let cached = null;
const clientInfo = (_a = args.client) !== null && _a !== void 0 ? _a : { name: 'hive-client', version: version_js_1.version };
const circuitBreakerConfig = (_b = args.circuitBreaker) !== null && _b !== void 0 ? _b : circuit_breaker_js_1.defaultCircuitBreakerConfiguration;
const endpoints = Array.isArray(args.endpoint) ? args.endpoint : [args.endpoint];
function runFetch(circuitBreaker, endpoint) {
var _a;
const signal = circuitBreaker.getSignal();
const headers = {
'X-Hive-CDN-Key': args.accessKey,
'User-Agent': `${clientInfo.name}/${clientInfo.version}`,
};
if (cacheETag) {
headers['If-None-Match'] = cacheETag;
}
return http_client_js_1.http.get(endpoint, {
headers,
isRequestOk,
retry: (_a = args.retry) !== null && _a !== void 0 ? _a : {
retries: 10,
maxTimeout: 200,
minTimeout: 1,
},
timeout: args.timeout,
logger,
fetchImplementation: args.fetch,
signal,
});
}
const circuitBreakers = endpoints.map(endpoint => {
const circuitBreaker = new circuit_js_1.default(async function fire() {
return await runFetch(circuitBreaker, endpoint);
}, Object.assign(Object.assign({}, circuitBreakerConfig), { timeout: false, autoRenewAbortController: true }));
return circuitBreaker;
});
async function attempt(breaker) {
var _a;
const response = await breaker.fire();
if (response.status === 304) {
if (cached !== null) {
return cached;
}
throw new Error('Unexpected 304 with no cache');
}
const contents = await response.text();
const result = {
hash: await (0, utils_js_1.createHash)('SHA-256').update(contents).digest('base64'),
contents,
schemaVersionId: (_a = response.headers.get('x-hive-schema-version-id')) !== null && _a !== void 0 ? _a : null,
};
const etag = response.headers.get('etag');
if (etag) {
cached = result;
cacheETag = etag;
}
return result;
}
return {
async fetch() {
for (const [index, breaker] of circuitBreakers.entries()) {
try {
return await attempt(breaker);
}
catch (error) {
logger.debug({ error });
if (index === circuitBreakers.length - 1) {
if (cached) {
return cached;
}
}
}
}
throw new Error('Could not retrieve artifact.');
},
dispose() {
circuitBreakers.forEach(breaker => breaker.shutdown());
},
};
}