diff --git a/src/fetchers/top-languages-fetcher.js b/src/fetchers/top-languages-fetcher.js index 181375d9e66bc..0a202d88d8db5 100644 --- a/src/fetchers/top-languages-fetcher.js +++ b/src/fetchers/top-languages-fetcher.js @@ -13,10 +13,10 @@ const fetcher = (variables, token) => { return request( { query: ` - query userInfo($login: String!) { + query userInfo($login: String!, $after: String) { user(login: $login) { # fetch only owner repos & not forks - repositories(ownerAffiliations: OWNER, isFork: false, first: 100) { + repositories(ownerAffiliations: OWNER, isFork: false, first: 100, after: $after) { nodes { name languages(first: 10, orderBy: {field: SIZE, direction: DESC}) { @@ -29,6 +29,10 @@ const fetcher = (variables, token) => { } } } + pageInfo { + hasNextPage + endCursor + } } } } @@ -49,14 +53,23 @@ const fetcher = (variables, token) => { async function fetchTopLanguages(username, exclude_repo = []) { if (!username) throw new MissingParamError(["username"]); - const res = await retryer(fetcher, { login: username }); + let repoNodes = []; + let hasNextPage = true; + let endCursor = null; + while (hasNextPage) { + const variables = { login: username, first: 100, after: endCursor }; + const res = await retryer(fetcher, variables); + + if (res.data.errors) { + logger.error(res.data.errors); + throw Error(res.data.errors[0].message || "Could not fetch user"); + } - if (res.data.errors) { - logger.error(res.data.errors); - throw Error(res.data.errors[0].message || "Could not fetch user"); + repoNodes.push(...res.data.data.user.repositories.nodes); + hasNextPage = res.data.data.user.repositories.pageInfo.hasNextPage; + endCursor = res.data.data.user.repositories.pageInfo.endCursor; } - let repoNodes = res.data.data.user.repositories.nodes; let repoToHide = {}; // populate repoToHide map for quick lookup @@ -96,14 +109,12 @@ async function fetchTopLanguages(username, exclude_repo = []) { }; }, {}); - const topLangs = Object.keys(repoNodes) + return Object.keys(repoNodes) .sort((a, b) => repoNodes[b].size - repoNodes[a].size) .reduce((result, key) => { result[key] = repoNodes[key]; return result; }, {}); - - return topLangs; } export { fetchTopLanguages }; diff --git a/tests/fetchTopLanguages.test.js b/tests/fetchTopLanguages.test.js index 24416cd294525..babf8520e7dd3 100644 --- a/tests/fetchTopLanguages.test.js +++ b/tests/fetchTopLanguages.test.js @@ -13,6 +13,12 @@ const data_langs = { data: { user: { repositories: { + edges: [ + { cursor: "1" }, + { cursor: "2" }, + { cursor: "3" }, + { cursor: "4" }, + ], nodes: [ { name: "test-repo-1", @@ -43,6 +49,10 @@ const data_langs = { }, }, ], + pageInfo: { + hasNextPage: false, + cursor: "cursor", + }, }, }, }, diff --git a/tests/top-langs.test.js b/tests/top-langs.test.js index b5232c29af3d5..31769e4fa1f1d 100644 --- a/tests/top-langs.test.js +++ b/tests/top-langs.test.js @@ -36,6 +36,64 @@ const data_langs = { }, }, ], + pageInfo: { + hasNextPage: false, + cursor: "cursor", + }, + }, + }, + }, +}; + +const firstPage = { + data: { + user: { + repositories: { + nodes: [ + { + languages: { + edges: [{ size: 150, node: { color: "#0f0", name: "HTML" } }], + }, + }, + { + languages: { + edges: [{ size: 100, node: { color: "#0f0", name: "HTML" } }], + }, + }, + ], + pageInfo: { + hasNextPage: true, + cursor: "cursor", + }, + }, + }, + }, +}; + +const secondPage = { + data: { + user: { + repositories: { + nodes: [ + { + languages: { + edges: [ + { size: 100, node: { color: "#0ff", name: "javascript" } }, + ], + }, + }, + { + languages: { + edges: [ + { size: 100, node: { color: "#0ff", name: "javascript" } }, + ], + }, + }, + ], + pageInfo: { + hasNextPage: false, + cursor: "cursor", + }, }, }, }, @@ -90,6 +148,29 @@ describe("Test /api/top-langs", () => { expect(res.send).toBeCalledWith(renderTopLanguages(langs)); }); + it("should test the request with two pages", async () => { + const req = { + query: { + username: "anuraghazra", + }, + }; + const res = { + setHeader: jest.fn(), + send: jest.fn(), + }; + + mock + .onPost("https://api.github.com/graphql") + .replyOnce(200, firstPage) + .onPost("https://api.github.com/graphql") + .replyOnce(200, secondPage); + + await topLangs(req, res); + + expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml"); + expect(res.send).toBeCalledWith(renderTopLanguages(langs)); + }); + it("should work with the query options", async () => { const req = { query: {