Optimize [GithubIssues] using GraphQL (#4107)

While playing around with a badge for Hacktoberfest I noticed the GitHub Issues badge fetches a huge amount of JSON to render just the total count. There doesn’t seem to be a way to limit the response size through the REST API, so I thought I’d switch this to use GraphQL instead.
This commit is contained in:
Paul Melnikow
2019-10-02 15:34:14 -04:00
committed by repo-ranger[bot]
parent e8d49f2504
commit a5b2f5436c

View File

@@ -1,10 +1,31 @@
'use strict'
const gql = require('graphql-tag')
const Joi = require('@hapi/joi')
const { metric } = require('../text-formatters')
const { nonNegativeInteger } = require('../validators')
const { GithubAuthV3Service } = require('./github-auth-service')
const { documentation, errorMessagesFor } = require('./github-helpers')
const { GithubAuthV4Service } = require('./github-auth-service')
const { documentation, transformErrors } = require('./github-helpers')
const issueCountSchema = Joi.object({
data: Joi.object({
repository: Joi.object({
issues: Joi.object({
totalCount: nonNegativeInteger,
}).required(),
}).required(),
}).required(),
}).required()
const pullRequestCountSchema = Joi.object({
data: Joi.object({
repository: Joi.object({
pullRequests: Joi.object({
totalCount: nonNegativeInteger,
}).required(),
}).required(),
}).required(),
}).required()
const isPRVariant = {
'issues-pr': true,
@@ -16,11 +37,7 @@ const isClosedVariant = {
'issues-pr-closed': true,
}
const schema = Joi.object({
total_count: nonNegativeInteger,
}).required()
module.exports = class GithubIssues extends GithubAuthV3Service {
module.exports = class GithubIssues extends GithubAuthV4Service {
static get category() {
return 'issue-tracking'
}
@@ -223,8 +240,8 @@ module.exports = class GithubIssues extends GithubAuthV3Service {
}
}
static render({ variant, numIssues, raw, label }) {
const state = isClosedVariant[variant] ? 'closed' : 'open'
static render({ isPR, isClosed, issueCount, raw, label }) {
const state = isClosed ? 'closed' : 'open'
let labelPrefix = ''
let messageSuffix = ''
@@ -238,35 +255,98 @@ module.exports = class GithubIssues extends GithubAuthV3Service {
const labelText = label
? `${isGhLabelMultiWord ? `"${label}"` : label} `
: ''
const labelSuffix = isPRVariant[variant] ? 'pull requests' : 'issues'
const labelSuffix = isPR ? 'pull requests' : 'issues'
return {
label: `${labelPrefix}${labelText}${labelSuffix}`,
message: `${metric(numIssues)} ${messageSuffix}`,
color: numIssues > 0 ? 'yellow' : 'brightgreen',
message: `${metric(issueCount)} ${messageSuffix}`,
color: issueCount > 0 ? 'yellow' : 'brightgreen',
}
}
async fetch({ variant, user, repo, label }) {
const isPR = isPRVariant[variant]
const isClosed = isClosedVariant[variant]
const query = `repo:${user}/${repo}${isPR ? ' is:pr' : ' is:issue'}${
isClosed ? ' is:closed' : ' is:open'
}${label ? ` label:"${label}"` : ''}`
const options = { qs: { q: query } }
return this._requestJson({
url: `/search/issues`,
options,
schema,
errorMessages: errorMessagesFor('repo not found'),
})
async fetch({ isPR, isClosed, user, repo, label }) {
const commonVariables = {
user,
repo,
labels: label ? [label] : undefined,
}
if (isPR) {
const {
data: {
repository: {
pullRequests: { totalCount },
},
},
} = await this._requestGraphql({
query: gql`
query(
$user: String!
$repo: String!
$states: [PullRequestState!]
$labels: [String!]
) {
repository(owner: $user, name: $repo) {
pullRequests(states: $states, labels: $labels) {
totalCount
}
}
}
`,
variables: {
...commonVariables,
states: isClosed ? ['MERGED', 'CLOSED'] : ['OPEN'],
},
schema: pullRequestCountSchema,
transformErrors,
})
return { issueCount: totalCount }
} else {
const {
data: {
repository: {
issues: { totalCount },
},
},
} = await this._requestGraphql({
query: gql`
query(
$user: String!
$repo: String!
$states: [IssueState!]
$labels: [String!]
) {
repository(owner: $user, name: $repo) {
issues(states: $states, labels: $labels) {
totalCount
}
}
}
`,
variables: {
...commonVariables,
states: isClosed ? ['CLOSED'] : ['OPEN'],
},
schema: issueCountSchema,
transformErrors,
})
return { issueCount: totalCount }
}
}
async handle({ variant, raw, user, repo, label }) {
const json = await this.fetch({ variant, user, repo, label })
const isPR = isPRVariant[variant]
const isClosed = isClosedVariant[variant]
const { issueCount } = await this.fetch({
isPR,
isClosed,
user,
repo,
label,
})
return this.constructor.render({
variant,
numIssues: json.total_count,
isPR,
isClosed,
issueCount,
raw,
label,
})