1 | const {URL} = require('url');
|
2 | const {memoize, get} = require('lodash');
|
3 | const Octokit = require('@octokit/rest');
|
4 | const pRetry = require('p-retry');
|
5 | const Bottleneck = require('bottleneck');
|
6 | const urljoin = require('url-join');
|
7 | const HttpProxyAgent = require('http-proxy-agent');
|
8 | const HttpsProxyAgent = require('https-proxy-agent');
|
9 |
|
10 | const {RETRY_CONF, RATE_LIMITS, GLOBAL_RATE_LIMIT} = require('./definitions/rate-limit');
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | const SKIP_RETRY_CODES = [400, 401, 403];
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 | const getThrottler = memoize((rate, globalThrottler) =>
|
27 | new Bottleneck({minTime: get(RATE_LIMITS, rate)}).chain(globalThrottler)
|
28 | );
|
29 |
|
30 | module.exports = ({githubToken, githubUrl, githubApiPathPrefix, proxy}) => {
|
31 | const baseUrl = githubUrl && urljoin(githubUrl, githubApiPathPrefix);
|
32 | const globalThrottler = new Bottleneck({minTime: GLOBAL_RATE_LIMIT});
|
33 | const github = new Octokit({
|
34 | auth: `token ${githubToken}`,
|
35 | baseUrl,
|
36 | request: {
|
37 | agent: proxy
|
38 | ? baseUrl && new URL(baseUrl).protocol.replace(':', '') === 'http'
|
39 | ? new HttpProxyAgent(proxy)
|
40 | : new HttpsProxyAgent(proxy)
|
41 | : undefined,
|
42 | },
|
43 | });
|
44 |
|
45 | github.hook.wrap('request', (request, options) => {
|
46 | const access = options.method === 'GET' ? 'read' : 'write';
|
47 | const rateCategory = options.url.startsWith('/search') ? 'search' : 'core';
|
48 | const limitKey = [rateCategory, RATE_LIMITS[rateCategory][access] && access].filter(Boolean).join('.');
|
49 |
|
50 | return pRetry(async () => {
|
51 | try {
|
52 | return await getThrottler(limitKey, globalThrottler).wrap(request)(options);
|
53 | } catch (error) {
|
54 | if (SKIP_RETRY_CODES.includes(error.status)) {
|
55 | throw new pRetry.AbortError(error);
|
56 | }
|
57 |
|
58 | throw error;
|
59 | }
|
60 | }, RETRY_CONF);
|
61 | });
|
62 |
|
63 | return github;
|
64 | };
|