@graphql-hive/core
Version:
95 lines (94 loc) • 3.56 kB
JavaScript
import CircuitBreaker from '../circuit-breaker/circuit.js';
import { version } from '../version.js';
import { defaultCircuitBreakerConfiguration, } from './circuit-breaker.js';
import { http } from './http-client.js';
import { chooseLogger, createHash } from './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.
*/
export function createCDNArtifactFetcher(args) {
var _a, _b;
const logger = chooseLogger(args.logger);
let cacheETag = null;
let cached = null;
const clientInfo = (_a = args.client) !== null && _a !== void 0 ? _a : { name: 'hive-client', version };
const circuitBreakerConfig = (_b = args.circuitBreaker) !== null && _b !== void 0 ? _b : 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.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 CircuitBreaker(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 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());
},
};
}