Skip to content

Commit

Permalink
fix: fetch all repos for for the stats card (#2100)
Browse files Browse the repository at this point in the history
* fetch all stars

* stop fetching when there are repos with zero stars

* remove not needed parameters from the query

* add docstring

* removed not needed mock

* style: update formatting

Co-authored-by: rickstaa <[email protected]>
  • Loading branch information
MatteoPierro and rickstaa authored Oct 4, 2022
1 parent acbc03d commit af97e57
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 18 deletions.
78 changes: 68 additions & 10 deletions src/fetchers/stats-fetcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ const fetcher = (variables, token) => {
totalCommitContributions
restrictedContributionsCount
}
repositoriesContributedTo(first: 1, contributionTypes: [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]) {
repositoriesContributedTo(contributionTypes: [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]) {
totalCount
}
pullRequests(first: 1) {
pullRequests {
totalCount
}
openIssues: issues(states: OPEN) {
Expand All @@ -44,14 +44,41 @@ const fetcher = (variables, token) => {
followers {
totalCount
}
repositories(first: 100, ownerAffiliations: OWNER, orderBy: {direction: DESC, field: STARGAZERS}) {
repositories(ownerAffiliations: OWNER) {
totalCount
}
}
}
`,
variables,
},
{
Authorization: `bearer ${token}`,
},
);
};

/**
* @param {import('axios').AxiosRequestHeaders} variables
* @param {string} token
*/
const repositoriesFetcher = (variables, token) => {
return request(
{
query: `
query userInfo($login: String!, $after: String) {
user(login: $login) {
repositories(first: 100, ownerAffiliations: OWNER, orderBy: {direction: DESC, field: STARGAZERS}, after: $after) {
nodes {
name
stargazers {
totalCount
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
Expand Down Expand Up @@ -99,6 +126,43 @@ const totalCommitsFetcher = async (username) => {
return 0;
};

/**
* Fetch all the stars for all the repositories of a given username
* @param {string} username
* @param {array} repoToHide
*/
const totalStarsFetcher = async (username, repoToHide) => {
let nodes = [];
let hasNextPage = true;
let endCursor = null;
while (hasNextPage) {
const variables = { login: username, first: 100, after: endCursor };
let res = await retryer(repositoriesFetcher, variables);

if (res.data.errors) {
logger.error(res.data.errors);
throw new CustomError(
res.data.errors[0].message || "Could not fetch user",
CustomError.USER_NOT_FOUND,
);
}

const allNodes = res.data.data.user.repositories.nodes;
const nodesWithStars = allNodes.filter(
(node) => node.stargazers.totalCount !== 0,
);
nodes.push(...nodesWithStars);
hasNextPage =
allNodes.length === nodesWithStars.length &&
res.data.data.user.repositories.pageInfo.hasNextPage;
endCursor = res.data.data.user.repositories.pageInfo.endCursor;
}

return nodes
.filter((data) => !repoToHide[data.name])
.reduce((prev, curr) => prev + curr.stargazers.totalCount, 0);
};

/**
* @param {string} username
* @param {boolean} count_private
Expand Down Expand Up @@ -166,13 +230,7 @@ async function fetchStats(
stats.contributedTo = user.repositoriesContributedTo.totalCount;

// Retrieve stars while filtering out repositories to be hidden
stats.totalStars = user.repositories.nodes
.filter((data) => {
return !repoToHide[data.name];
})
.reduce((prev, curr) => {
return prev + curr.stargazers.totalCount;
}, 0);
stats.totalStars = await totalStarsFetcher(username, repoToHide);

stats.rank = calculateRank({
totalCommits: stats.totalCommits,
Expand Down
20 changes: 18 additions & 2 deletions tests/api.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,20 @@ const data = {
followers: { totalCount: 0 },
repositories: {
totalCount: 1,
},
},
},
};

const repositoriesData = {
data: {
user: {
repositories: {
nodes: [{ stargazers: { totalCount: 100 } }],
pageInfo: {
hasNextPage: false,
cursor: "cursor",
},
},
},
},
Expand Down Expand Up @@ -70,7 +83,11 @@ const faker = (query, data) => {
setHeader: jest.fn(),
send: jest.fn(),
};
mock.onPost("https://api.github.com/graphql").reply(200, data);
mock
.onPost("https://api.github.com/graphql")
.replyOnce(200, data)
.onPost("https://api.github.com/graphql")
.replyOnce(200, repositoriesData);

return { req, res };
};
Expand Down Expand Up @@ -138,7 +155,6 @@ describe("Test /api/", () => {

it("should have proper cache", async () => {
const { req, res } = faker({}, data);
mock.onPost("https://api.github.com/graphql").reply(200, data);

await api(req, res);

Expand Down
95 changes: 89 additions & 6 deletions tests/fetchStats.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,61 @@ const data = {
followers: { totalCount: 100 },
repositories: {
totalCount: 5,
},
},
},
};

const firstRepositoriesData = {
data: {
user: {
repositories: {
nodes: [
{ name: "test-repo-1", stargazers: { totalCount: 100 } },
{ name: "test-repo-2", stargazers: { totalCount: 100 } },
{ name: "test-repo-3", stargazers: { totalCount: 100 } },
],
pageInfo: {
hasNextPage: true,
cursor: "cursor",
},
},
},
},
};

const secondRepositoriesData = {
data: {
user: {
repositories: {
nodes: [
{ name: "test-repo-4", stargazers: { totalCount: 50 } },
{ name: "test-repo-5", stargazers: { totalCount: 50 } },
],
pageInfo: {
hasNextPage: false,
cursor: "cursor",
},
},
},
},
};

const repositoriesWithZeroStarsData = {
data: {
user: {
repositories: {
nodes: [
{ name: "test-repo-1", stargazers: { totalCount: 100 } },
{ name: "test-repo-2", stargazers: { totalCount: 100 } },
{ name: "test-repo-3", stargazers: { totalCount: 100 } },
{ name: "test-repo-4", stargazers: { totalCount: 0 } },
{ name: "test-repo-5", stargazers: { totalCount: 0 } },
],
pageInfo: {
hasNextPage: true,
cursor: "cursor",
},
},
},
},
Expand All @@ -44,14 +92,22 @@ const error = {

const mock = new MockAdapter(axios);

beforeEach(() => {
mock
.onPost("https://api.github.com/graphql")
.replyOnce(200, data)
.onPost("https://api.github.com/graphql")
.replyOnce(200, firstRepositoriesData)
.onPost("https://api.github.com/graphql")
.replyOnce(200, secondRepositoriesData);
});

afterEach(() => {
mock.reset();
});

describe("Test fetchStats", () => {
it("should fetch correct stats", async () => {
mock.onPost("https://api.github.com/graphql").reply(200, data);

let stats = await fetchStats("anuraghazra");
const rank = calculateRank({
totalCommits: 100,
Expand All @@ -74,7 +130,38 @@ describe("Test fetchStats", () => {
});
});

it("should stop fetching when there are repos with zero stars", async () => {
mock.reset();
mock
.onPost("https://api.github.com/graphql")
.replyOnce(200, data)
.onPost("https://api.github.com/graphql")
.replyOnce(200, repositoriesWithZeroStarsData);

let stats = await fetchStats("anuraghazra");
const rank = calculateRank({
totalCommits: 100,
totalRepos: 5,
followers: 100,
contributions: 61,
stargazers: 300,
prs: 300,
issues: 200,
});

expect(stats).toStrictEqual({
contributedTo: 61,
name: "Anurag Hazra",
totalCommits: 100,
totalIssues: 200,
totalPRs: 300,
totalStars: 300,
rank,
});
});

it("should throw error", async () => {
mock.reset();
mock.onPost("https://api.github.com/graphql").reply(200, error);

await expect(fetchStats("anuraghazra")).rejects.toThrow(
Expand All @@ -83,8 +170,6 @@ describe("Test fetchStats", () => {
});

it("should fetch and add private contributions", async () => {
mock.onPost("https://api.github.com/graphql").reply(200, data);

let stats = await fetchStats("anuraghazra", true);
const rank = calculateRank({
totalCommits: 150,
Expand All @@ -108,7 +193,6 @@ describe("Test fetchStats", () => {
});

it("should fetch total commits", async () => {
mock.onPost("https://api.github.com/graphql").reply(200, data);
mock
.onGet("https://api.github.com/search/commits?q=author:anuraghazra")
.reply(200, { total_count: 1000 });
Expand Down Expand Up @@ -136,7 +220,6 @@ describe("Test fetchStats", () => {
});

it("should exclude stars of the `test-repo-1` repository", async () => {
mock.onPost("https://api.github.com/graphql").reply(200, data);
mock
.onGet("https://api.github.com/search/commits?q=author:anuraghazra")
.reply(200, { total_count: 1000 });
Expand Down

1 comment on commit af97e57

@vercel
Copy link

@vercel vercel bot commented on af97e57 Oct 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.