UNPKG

@graphql-hive/core

Version:
95 lines (94 loc) 3.56 kB
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()); }, }; }