github.js

const Bottleneck = require('bottleneck')
const Octokit = require('@octokit/rest')

/**
 * the [@octokit/rest Node.js module](https://github.com/octokit/rest.js),
 * which wraps the [GitHub API](https://developer.github.com/v3/) and allows
 * you to do almost anything programmatically that you can do through a web
 * browser.
 * @typedef github
 * @see {@link https://github.com/octokit/rest.js}
 */

const defaultCallback = response => response
async function paginate (octokit, responsePromise, callback = defaultCallback) {
  let collection = []
  let getNextPage = true
  let done = () => {
    getNextPage = false
  }
  let response = await responsePromise
  collection = collection.concat(await callback(response, done))
  // eslint-disable-next-line no-unmodified-loop-condition
  while (getNextPage && octokit.hasNextPage(response)) {
    response = await octokit.getNextPage(response)
    collection = collection.concat(await callback(response, done))
  }
  return collection
}

function EnhancedGitHubClient (options) {
  const octokit = Octokit(options)
  const limiter = options.limiter || new Bottleneck({ maxConcurrent: 1, minTime: 1000 })
  const logger = options.logger
  const noop = () => Promise.resolve()

  octokit.hook.before('request', limiter.schedule.bind(limiter, noop))
  octokit.hook.error('request', (error, options) => {
    const {method, url, headers, ...params} = options
    const msg = `GitHub request: ${method} ${url} - ${error.code} ${error.status}`
    logger.debug({params}, msg)
    throw error
  })
  octokit.hook.after('request', (result, options) => {
    const {method, url, headers, ...params} = options
    const msg = `GitHub request: ${method} ${url} - ${result.meta.status}`
    logger.debug({params}, msg)
  })
  octokit.paginate = paginate.bind(null, octokit)

  return octokit
}

module.exports = EnhancedGitHubClient