diff --git a/src/application.ts b/src/application.ts index d7527be3..1061de7a 100644 --- a/src/application.ts +++ b/src/application.ts @@ -34,6 +34,7 @@ export class Application { public cache: Cache public router: express.Router public log: LoggerWithTarget + public throttleOptions: any private githubToken?: string @@ -46,6 +47,20 @@ export class Application { this.router = opts.router || express.Router() // you can do this? this.githubToken = opts.githubToken + if (process.env.REDIS_URL) { + const Bottleneck = require('bottleneck') + const Redis = require('ioredis') + + const client = new Redis(process.env.REDIS_URL) + const connection = new Bottleneck.IORedisConnection({ client }) + connection.on('error', this.log.error) + + this.throttleOptions = { + Bottleneck, + connection + } + } + if (opts.catchErrors) { // Deprecated since 7.2.0 // tslint:disable-next-line:no-console @@ -195,26 +210,35 @@ export class Application { // if installation ID passed, instantiate and authenticate Octokit, then cache the instance // so that it can be used across received webhook events. if (id) { + const options = { + auth: async () => { + const accessToken = await this.app.getInstallationAccessToken({ installationId: id }) + return `token ${accessToken}` + }, + baseUrl: process.env.GHE_HOST && `https://${process.env.GHE_HOST}/api/v3`, + logger: log.child({ name: 'github', installation: String(id) }) + } + + if (this.throttleOptions) { + return GitHubAPI({ + ...options, + throttle: { + id, + ...this.throttleOptions + } + }) + } + // Cache for 1 minute less than GitHub expiry const installationTokenTTL = parseInt(process.env.INSTALLATION_TOKEN_TTL || '3540', 10) - - return this.cache.wrap(`app:${id}`, async () => { - return GitHubAPI({ - auth: async () => { - const accessToken = await this.app.getInstallationAccessToken({ installationId: id }) - return `token ${accessToken}` - }, - baseUrl: process.env.GHE_HOST && `https://${process.env.GHE_HOST}/api/v3`, - logger: log.child({ name: 'github', installation: String(id) }) - }) - }, { ttl: installationTokenTTL }) + return this.cache.wrap(`app:${id}`, () => GitHubAPI(options), { ttl: installationTokenTTL }) } const token = this.githubToken || this.app.getSignedJsonWebToken() const github = GitHubAPI({ auth: `Bearer ${token}`, baseUrl: process.env.GHE_HOST && `https://${process.env.GHE_HOST}/api/v3`, - logger: log.child({ name: 'github', installation: String(id) }) + logger: log.child({ name: 'github' }) }) return github diff --git a/src/github/index.ts b/src/github/index.ts index 9be52f8f..058a2086 100644 --- a/src/github/index.ts +++ b/src/github/index.ts @@ -21,10 +21,10 @@ const ProbotOctokit = Octokit */ export function GitHubAPI (options: Options = {} as any) { const octokit = new ProbotOctokit(Object.assign(options, { - throttle: { + throttle: Object.assign({ onAbuseLimit: (retryAfter: number) => options.logger.warn(`Abuse limit hit, retrying in ${retryAfter} seconds`), onRateLimit: (retryAfter: number) => options.logger.warn(`Rate limit hit, retrying in ${retryAfter} seconds`) - } + }, options.throttle) })) as GitHubAPI addPagination(octokit)