Skip to content

Commit 429d65a

Browse files
committed
refactor: refactored retryer logic & handled invalid tokens
1 parent 3d8dea9 commit 429d65a

File tree

6 files changed

+112
-45
lines changed

6 files changed

+112
-45
lines changed

api/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ module.exports = async (req, res) => {
1818
} = req.query;
1919
let stats;
2020

21-
res.setHeader("Cache-Control", "public, max-age=300");
21+
res.setHeader("Cache-Control", "public, max-age=1800");
2222
res.setHeader("Content-Type", "image/svg+xml");
2323

2424
try {

api/pin.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ module.exports = async (req, res) => {
1515

1616
let repoData;
1717

18-
res.setHeader("Cache-Control", "public, max-age=300");
18+
res.setHeader("Cache-Control", "public, max-age=1800");
1919
res.setHeader("Content-Type", "image/svg+xml");
2020

2121
try {

src/fetchRepo.js

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
const { request } = require("./utils");
2+
const retryer = require("./retryer");
23

3-
async function fetchRepo(username, reponame) {
4-
if (!username || !reponame) {
5-
throw new Error("Invalid username or reponame");
6-
}
7-
8-
const res = await request(
4+
const fetcher = (variables, token) => {
5+
return request(
96
{
107
query: `
118
fragment RepoInfo on Repository {
@@ -34,15 +31,20 @@ async function fetchRepo(username, reponame) {
3431
}
3532
}
3633
`,
37-
variables: {
38-
login: username,
39-
repo: reponame,
40-
},
34+
variables,
4135
},
4236
{
43-
Authorization: `bearer ${process.env.PAT_1}`,
37+
Authorization: `bearer ${token}`,
4438
}
4539
);
40+
};
41+
42+
async function fetchRepo(username, reponame) {
43+
if (!username || !reponame) {
44+
throw new Error("Invalid username or reponame");
45+
}
46+
47+
let res = await retryer(fetcher, { login: username, repo: reponame });
4648

4749
const data = res.data.data;
4850

src/fetchStats.js

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
const { request } = require("./utils");
2+
const retryer = require("./retryer");
23
const calculateRank = require("./calculateRank");
34
require("dotenv").config();
45

5-
// creating a fetcher function to reduce duplication
6-
const fetcher = (username, token) => {
6+
const fetcher = (variables, token) => {
77
return request(
88
{
99
query: `
@@ -37,43 +37,15 @@ const fetcher = (username, token) => {
3737
}
3838
}
3939
`,
40-
variables: { login: username },
40+
variables,
4141
},
4242
{
43-
// set the token
4443
Authorization: `bearer ${token}`,
4544
}
4645
);
4746
};
4847

49-
async function retryer(username, RETRIES) {
50-
try {
51-
console.log(`Trying PAT_${RETRIES + 1}`);
52-
53-
// try to fetch with the first token since RETRIES is 0 index i'm adding +1
54-
let response = await fetcher(username, process.env[`PAT_${RETRIES + 1}`]);
55-
56-
// if rate limit is hit increase the RETRIES and recursively call the retryer
57-
// with username, and current RETRIES
58-
if (
59-
response.data.errors &&
60-
response.data.errors[0].type === "RATE_LIMITED"
61-
) {
62-
console.log(`PAT_${RETRIES} Failed`);
63-
RETRIES++;
64-
// directly return from the function
65-
return await retryer(username, RETRIES);
66-
}
67-
68-
// finally return the response
69-
return response;
70-
} catch (err) {
71-
console.log(err);
72-
}
73-
}
74-
7548
async function fetchStats(username) {
76-
let RETRIES = 0;
7749
if (!username) throw Error("Invalid username");
7850

7951
const stats = {
@@ -86,7 +58,7 @@ async function fetchStats(username) {
8658
rank: { level: "C", score: 0 },
8759
};
8860

89-
let res = await retryer(username, RETRIES);
61+
let res = await retryer(fetcher, { login: username });
9062

9163
if (res.data.errors) {
9264
console.log(res.data.errors);

src/retryer.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
const retryer = async (fetcher, variables, retries = 0) => {
2+
if (retries > 7) {
3+
throw new Error("Maximum retries exceeded");
4+
}
5+
try {
6+
console.log(`Trying PAT_${retries + 1}`);
7+
8+
// try to fetch with the first token since RETRIES is 0 index i'm adding +1
9+
let response = await fetcher(
10+
variables,
11+
process.env[`PAT_${retries + 1}`],
12+
retries
13+
);
14+
15+
// prettier-ignore
16+
const isRateExceeded = response.data.errors && response.data.errors[0].type === "RATE_LIMITED";
17+
18+
// if rate limit is hit increase the RETRIES and recursively call the retryer
19+
// with username, and current RETRIES
20+
if (isRateExceeded) {
21+
console.log(`PAT_${retries + 1} Failed`);
22+
retries++;
23+
// directly return from the function
24+
return retryer(fetcher, variables, retries);
25+
}
26+
27+
// finally return the response
28+
return response;
29+
} catch (err) {
30+
// prettier-ignore
31+
// also checking for bad credentials if any tokens gets invalidated
32+
const isBadCredential = err.response.data && err.response.data.message === "Bad credentials";
33+
34+
if (isBadCredential) {
35+
console.log(`PAT_${retries + 1} Failed`);
36+
retries++;
37+
// directly return from the function
38+
return retryer(fetcher, variables, retries);
39+
}
40+
}
41+
};
42+
43+
module.exports = retryer;

tests/retryer.test.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
require("@testing-library/jest-dom");
2+
const retryer = require("../src/retryer");
3+
4+
const fetcher = jest.fn((variables, token) => {
5+
console.log(variables, token);
6+
return new Promise((res, rej) => res({ data: "ok" }));
7+
});
8+
9+
const fetcherFail = jest.fn(() => {
10+
return new Promise((res, rej) =>
11+
res({ data: { errors: [{ type: "RATE_LIMITED" }] } })
12+
);
13+
});
14+
15+
const fetcherFailOnSecondTry = jest.fn((_vars, _token, retries) => {
16+
return new Promise((res, rej) => {
17+
// faking rate limit
18+
if (retries < 1) {
19+
return res({ data: { errors: [{ type: "RATE_LIMITED" }] } });
20+
}
21+
return res({ data: "ok" });
22+
});
23+
});
24+
25+
describe("Test Retryer", () => {
26+
it("retryer should return value and have zero retries on first try", async () => {
27+
let res = await retryer(fetcher, {});
28+
29+
expect(fetcher).toBeCalledTimes(1);
30+
expect(res).toStrictEqual({ data: "ok" });
31+
});
32+
33+
it("retryer should return value and have 2 retries", async () => {
34+
let res = await retryer(fetcherFailOnSecondTry, {});
35+
36+
expect(fetcherFailOnSecondTry).toBeCalledTimes(2);
37+
expect(res).toStrictEqual({ data: "ok" });
38+
});
39+
40+
it("retryer should throw error if maximum retries reached", async () => {
41+
let res;
42+
43+
try {
44+
res = await retryer(fetcherFail, {});
45+
} catch (err) {
46+
expect(fetcherFail).toBeCalledTimes(8);
47+
expect(err.message).toBe("Maximum retries exceeded");
48+
}
49+
});
50+
});

0 commit comments

Comments
 (0)