fix: github rate limiter with multiple PATs

This commit is contained in:
anuraghazra 2020-07-15 14:59:25 +05:30
parent 2efb399f33
commit 3d8dea90e2
6 changed files with 78 additions and 25 deletions

View File

@ -18,7 +18,9 @@ module.exports = async (req, res) => {
} = req.query; } = req.query;
let stats; let stats;
res.setHeader("Cache-Control", "public, max-age=300");
res.setHeader("Content-Type", "image/svg+xml"); res.setHeader("Content-Type", "image/svg+xml");
try { try {
stats = await fetchStats(username); stats = await fetchStats(username);
} catch (err) { } catch (err) {

View File

@ -14,6 +14,8 @@ module.exports = async (req, res) => {
} = req.query; } = req.query;
let repoData; let repoData;
res.setHeader("Cache-Control", "public, max-age=300");
res.setHeader("Content-Type", "image/svg+xml"); res.setHeader("Content-Type", "image/svg+xml");
try { try {

View File

@ -5,8 +5,9 @@ async function fetchRepo(username, reponame) {
throw new Error("Invalid username or reponame"); throw new Error("Invalid username or reponame");
} }
const res = await request({ const res = await request(
query: ` {
query: `
fragment RepoInfo on Repository { fragment RepoInfo on Repository {
name name
stargazers { stargazers {
@ -33,11 +34,15 @@ async function fetchRepo(username, reponame) {
} }
} }
`, `,
variables: { variables: {
login: username, login: username,
repo: reponame, repo: reponame,
},
}, },
}); {
Authorization: `bearer ${process.env.PAT_1}`,
}
);
const data = res.data.data; const data = res.data.data;

View File

@ -2,25 +2,25 @@ const { request } = require("./utils");
const calculateRank = require("./calculateRank"); const calculateRank = require("./calculateRank");
require("dotenv").config(); require("dotenv").config();
async function fetchStats(username) { // creating a fetcher function to reduce duplication
if (!username) throw Error("Invalid username"); const fetcher = (username, token) => {
return request(
const res = await request({ {
query: ` query: `
query userInfo($login: String!) { query userInfo($login: String!) {
user(login: $login) { user(login: $login) {
name name
login login
repositoriesContributedTo(first: 100, contributionTypes: [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]) {
totalCount
}
contributionsCollection { contributionsCollection {
totalCommitContributions totalCommitContributions
} }
pullRequests(first: 100) { repositoriesContributedTo(first: 1, contributionTypes: [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]) {
totalCount totalCount
} }
issues(first: 100) { pullRequests(first: 1) {
totalCount
}
issues(first: 1) {
totalCount totalCount
} }
followers { followers {
@ -36,9 +36,45 @@ async function fetchStats(username) {
} }
} }
} }
`, `,
variables: { login: username }, variables: { login: username },
}); },
{
// set the token
Authorization: `bearer ${token}`,
}
);
};
async function retryer(username, RETRIES) {
try {
console.log(`Trying PAT_${RETRIES + 1}`);
// try to fetch with the first token since RETRIES is 0 index i'm adding +1
let response = await fetcher(username, process.env[`PAT_${RETRIES + 1}`]);
// if rate limit is hit increase the RETRIES and recursively call the retryer
// with username, and current RETRIES
if (
response.data.errors &&
response.data.errors[0].type === "RATE_LIMITED"
) {
console.log(`PAT_${RETRIES} Failed`);
RETRIES++;
// directly return from the function
return await retryer(username, RETRIES);
}
// finally return the response
return response;
} catch (err) {
console.log(err);
}
}
async function fetchStats(username) {
let RETRIES = 0;
if (!username) throw Error("Invalid username");
const stats = { const stats = {
name: "", name: "",
@ -47,12 +83,14 @@ async function fetchStats(username) {
totalIssues: 0, totalIssues: 0,
totalStars: 0, totalStars: 0,
contributedTo: 0, contributedTo: 0,
rank: "C", rank: { level: "C", score: 0 },
}; };
let res = await retryer(username, RETRIES);
if (res.data.errors) { if (res.data.errors) {
console.log(res.data.errors); console.log(res.data.errors);
throw Error("Could not fetch user"); throw Error(res.data.errors[0].message || "Could not fetch user");
} }
const user = res.data.data.user; const user = res.data.data.user;

View File

@ -33,13 +33,13 @@ function isValidHexColor(hexColor) {
).test(hexColor); ).test(hexColor);
} }
function request(data) { function request(data, headers) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
axios({ axios({
url: "https://api.github.com/graphql", url: "https://api.github.com/graphql",
method: "post", method: "post",
headers: { headers: {
Authorization: `bearer ${process.env.GITHUB_TOKEN}`, ...headers,
}, },
data, data,
}) })
@ -48,4 +48,10 @@ function request(data) {
}); });
} }
module.exports = { renderError, kFormatter, encodeHTML, isValidHexColor, request }; module.exports = {
renderError,
kFormatter,
encodeHTML,
isValidHexColor,
request,
};

View File

@ -74,7 +74,7 @@ describe("Test fetchStats", () => {
mock.onPost("https://api.github.com/graphql").reply(200, error); mock.onPost("https://api.github.com/graphql").reply(200, error);
await expect(fetchStats("anuraghazra")).rejects.toThrow( await expect(fetchStats("anuraghazra")).rejects.toThrow(
"Could not fetch user" "Could not resolve to a User with the login of 'noname'."
); );
}); });
}); });