UNPKG

@backstage/integration

Version:

Helpers for managing integrations towards external systems

139 lines (133 loc) 4.11 kB
'use strict'; var helpers = require('../helpers.cjs.js'); var config = require('./config.cjs.js'); var pThrottle = require('p-throttle'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; } var pThrottle__default = /*#__PURE__*/_interopDefaultCompat(pThrottle); class GitLabIntegration { constructor(integrationConfig) { this.integrationConfig = integrationConfig; this.fetchImpl = this.createFetchStrategy(); } static factory = ({ config: config$1 }) => { const configs = config.readGitLabIntegrationConfigs( config$1.getOptionalConfigArray("integrations.gitlab") ?? [] ); return helpers.basicIntegrations( configs.map((c) => new GitLabIntegration(c)), (i) => i.config.host ); }; fetchImpl; get type() { return "gitlab"; } get title() { return this.integrationConfig.host; } get config() { return this.integrationConfig; } resolveUrl(options) { return helpers.defaultScmResolveUrl(options); } resolveEditUrl(url) { return replaceGitLabUrlType(url, "edit"); } fetch(input, init) { return this.fetchImpl(input, init); } createFetchStrategy() { let fetchFn = (url, options) => fetch(url, options); const retryConfig = this.integrationConfig.retry; if (retryConfig) { fetchFn = this.withRetry(fetchFn, retryConfig); if (retryConfig.maxApiRequestsPerMinute && retryConfig.maxApiRequestsPerMinute > 0) { fetchFn = pThrottle__default.default({ limit: retryConfig.maxApiRequestsPerMinute, interval: 6e4 })(fetchFn); } } return fetchFn; } withRetry(fetchFn, retryConfig) { const maxRetries = retryConfig?.maxRetries ?? 0; const retryStatusCodes = retryConfig?.retryStatusCodes ?? []; if (maxRetries <= 0 || retryStatusCodes.length === 0) { return fetchFn; } const backoffDelay = (a) => Math.min(100 * Math.pow(2, a - 1), 1e4); return async (url, options) => { const abortSignal = options?.signal; let attempt = 0; for (; ; ) { let response; try { response = await fetchFn(url, options); } catch (e) { if (abortSignal?.aborted) throw e; if (attempt++ >= maxRetries) throw e; await sleep(backoffDelay(attempt), abortSignal); if (abortSignal?.aborted) throw e; continue; } if (!retryStatusCodes.includes(response.status)) { return response; } if (attempt++ >= maxRetries) { return response; } const delay = parseRetryAfterMs( response.headers.get("Retry-After"), backoffDelay(attempt) ); await response.body?.cancel().catch(() => { }); await sleep(delay, abortSignal); if (abortSignal?.aborted) return response; } }; } } function parseRetryAfterMs(headerValue, fallbackMs) { if (!headerValue) { return fallbackMs; } if (/^\d+$/.test(headerValue)) { return Number(headerValue) * 1e3; } if (headerValue.includes(",")) { const dateMs = Date.parse(headerValue); if (Number.isFinite(dateMs)) { const deltaMs = dateMs - Date.now(); return deltaMs > 0 ? deltaMs : 0; } } return fallbackMs; } async function sleep(durationMs, abortSignal) { if (abortSignal?.aborted) { return; } await new Promise((resolve) => { let timeoutHandle = void 0; const done = () => { if (timeoutHandle) { clearTimeout(timeoutHandle); } abortSignal?.removeEventListener("abort", done); resolve(); }; timeoutHandle = setTimeout(done, durationMs); abortSignal?.addEventListener("abort", done); }); } function replaceGitLabUrlType(url, type) { return url.replace(/\/\-\/(blob|tree|edit)\//, `/-/${type}/`); } exports.GitLabIntegration = GitLabIntegration; exports.parseRetryAfterMs = parseRetryAfterMs; exports.replaceGitLabUrlType = replaceGitLabUrlType; exports.sleep = sleep; //# sourceMappingURL=GitLabIntegration.cjs.js.map