Repository: anuraghazra/github-readme-stats
Pull request #3208 (2023-09-05 09:23:22)

feature: fetch only requested data from GitHub GraphQL API to reduce load

vercel[bot]: [vc]: #YhLLc+Hkw8gBXQTt2jfyDSqNyKFxPQ3H5LNPdlyuCdU=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJnaXRodWItcmVhZG1lLXN0YXRzIiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL2dpdGh1Yi1yZWFkbWUtc3RhdHMtdGVhbS9naXRodWItcmVhZG1lLXN0YXRzLzdDQnZQNGRqQU5nbW5la3JNMmNIYW56eUhDTHYiLCJwcmV2aWV3VXJsIjoiZ2l0aHViLXJlYWRtZS1zdGF0cy1naXQtZmVhdHVyZS1jNGQ5YWQtZ2l0aHViLXJlYWRtZS1zdGF0cy10ZWFtLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQifV19
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Updated (UTC) |
| :--- | :----- | :------ | :------ |
| **github-readme-stats** | ✅ Ready ([Inspect](https://vercel.com/github-readme-stats-team/github-readme-stats/7CBvP4djANgmnekrM2cHanzyHCLv)) | [Visit Preview](https://github-readme-stats-git-feature-c4d9ad-github-readme-stats-team.vercel.app) | Sep 5, 2023 9:23am |


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/anuraghazra/github-readme-stats/pull/3208?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Anurag+Hazra) Report
Patch coverage: **`100.00%`** and no project coverage change.
> Comparison is base [(`659077e`)](https://app.codecov.io/gh/anuraghazra/github-readme-stats/commit/659077ec44ed0a8ae7618071c041fcf24ab49a18?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Anurag+Hazra) 98.73% compared to head [(`63edd91`)](https://app.codecov.io/gh/anuraghazra/github-readme-stats/pull/3208?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Anurag+Hazra) 98.73%.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##           master    #3208   +/-   ##
=======================================
  Coverage   98.73%   98.73%           
=======================================
  Files          27       27           
  Lines        5831     5870   +39     
  Branches      526      531    +5     
=======================================
+ Hits         5757     5796   +39     
  Misses         71       71           
  Partials        3        3           
```


| [Files Changed](https://app.codecov.io/gh/anuraghazra/github-readme-stats/pull/3208?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Anurag+Hazra) | Coverage Δ | |
|---|---|---|
| [api/index.js](https://app.codecov.io/gh/anuraghazra/github-readme-stats/pull/3208?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Anurag+Hazra#diff-YXBpL2luZGV4Lmpz) | `100.00% <100.00%> (ø)` | |
| [src/fetchers/stats-fetcher.js](https://app.codecov.io/gh/anuraghazra/github-readme-stats/pull/3208?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Anurag+Hazra#diff-c3JjL2ZldGNoZXJzL3N0YXRzLWZldGNoZXIuanM=) | `95.06% <100.00%> (+0.57%)` | :arrow_up: |


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/anuraghazra/github-readme-stats/pull/3208?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Anurag+Hazra).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Anurag+Hazra).

rickstaa: @qwerty541 Feel free to merge this after you have fixed the conflicts 🚀!

Patch:
diff --git a/api/index.js b/api/index.js
index 25e4151..adfd331 100644
--- a/api/index.js
+++ b/api/index.js
@@ -50,10 +50,15 @@ export default async (req, res) => {
   }
 
   try {
+    const showStats = parseArray(show);
     const stats = await fetchStats(
       username,
       parseBoolean(include_all_commits),
       parseArray(exclude_repo),
+      showStats.includes("prs_merged") ||
+        showStats.includes("prs_merged_percentage"),
+      showStats.includes("discussions_started"),
+      showStats.includes("discussions_answered"),
     );
 
     let cacheSeconds = clampValue(
@@ -96,7 +101,7 @@ export default async (req, res) => {
         locale: locale ? locale.toLowerCase() : null,
         disable_animations: parseBoolean(disable_animations),
         rank_icon,
-        show: parseArray(show),
+        show: showStats,
       }),
     );
   } catch (err) {
diff --git a/src/fetchers/stats-fetcher.js b/src/fetchers/stats-fetcher.js
index 0f770d8..1d6aebf 100644
--- a/src/fetchers/stats-fetcher.js
+++ b/src/fetchers/stats-fetcher.js
@@ -40,7 +40,7 @@ const GRAPHQL_REPOS_QUERY = `
 `;
 
 const GRAPHQL_STATS_QUERY = `
-  query userInfo($login: String!, $after: String) {
+  query userInfo($login: String!, $after: String, $includeMergedPullRequests: Boolean!, $includeDiscussions: Boolean!, $includeDiscussionsAnswers: Boolean!) {
     user(login: $login) {
       name
       login
@@ -54,7 +54,7 @@ const GRAPHQL_STATS_QUERY = `
       pullRequests(first: 1) {
         totalCount
       }
-      mergedPullRequests: pullRequests(states: MERGED) {
+      mergedPullRequests: pullRequests(states: MERGED) @include(if: $includeMergedPullRequests) {
         totalCount
       }
       openIssues: issues(states: OPEN) {
@@ -66,10 +66,10 @@ const GRAPHQL_STATS_QUERY = `
       followers {
         totalCount
       }
-      repositoryDiscussions {
+      repositoryDiscussions @include(if: $includeDiscussions) {
         totalCount
       }
-      repositoryDiscussionComments(onlyAnswers: true) {
+      repositoryDiscussionComments(onlyAnswers: true) @include(if: $includeDiscussionsAnswers) {
         totalCount
       }
       ${GRAPHQL_REPOS_FIELD}
@@ -104,17 +104,33 @@ const fetcher = (variables, token) => {
 /**
  * Fetch stats information for a given username.
  *
- * @param {string} username Github username.
+ * @param {object} variables Fetcher variables.
+ * @param {string} variables.username Github username.
+ * @param {boolean} variables.includeMergedPullRequests Include merged pull requests.
+ * @param {boolean} variables.includeDiscussions Include discussions.
+ * @param {boolean} variables.includeDiscussionsAnswers Include discussions answers.
  * @returns {Promise<AxiosResponse>} Axios response.
  *
  * @description This function supports multi-page fetching if the 'FETCH_MULTI_PAGE_STARS' environment variable is set to true.
  */
-const statsFetcher = async (username) => {
+const statsFetcher = async ({
+  username,
+  includeMergedPullRequests,
+  includeDiscussions,
+  includeDiscussionsAnswers,
+}) => {
   let stats;
   let hasNextPage = true;
   let endCursor = null;
   while (hasNextPage) {
-    const variables = { login: username, first: 100, after: endCursor };
+    const variables = {
+      login: username,
+      first: 100,
+      after: endCursor,
+      includeMergedPullRequests,
+      includeDiscussions,
+      includeDiscussionsAnswers,
+    };
     let res = await retryer(fetcher, variables);
     if (res.data.errors) {
       return res;
@@ -198,12 +214,18 @@ const totalCommitsFetcher = async (username) => {
  * @param {string} username GitHub username.
  * @param {boolean} include_all_commits Include all commits.
  * @param {string[]} exclude_repo Repositories to exclude.
+ * @param {boolean} include_merged_pull_requests Include merged pull requests.
+ * @param {boolean} include_discussions Include discussions.
+ * @param {boolean} include_discussions_answers Include discussions answers.
  * @returns {Promise<StatsData>} Stats data.
  */
 const fetchStats = async (
   username,
   include_all_commits = false,
   exclude_repo = [],
+  include_merged_pull_requests = false,
+  include_discussions = false,
+  include_discussions_answers = false,
 ) => {
   if (!username) {
     throw new MissingParamError(["username"]);
@@ -224,7 +246,12 @@ const fetchStats = async (
     rank: { level: "C", percentile: 100 },
   };
 
-  let res = await statsFetcher(username);
+  let res = await statsFetcher({
+    username,
+    includeMergedPullRequests: include_merged_pull_requests,
+    includeDiscussions: include_discussions,
+    includeDiscussionsAnswers: include_discussions_answers,
+  });
 
   // Catch GraphQL errors.
   if (res.data.errors) {
@@ -259,14 +286,21 @@ const fetchStats = async (
   }
 
   stats.totalPRs = user.pullRequests.totalCount;
-  stats.totalPRsMerged = user.mergedPullRequests.totalCount;
-  stats.mergedPRsPercentage =
-    (user.mergedPullRequests.totalCount / user.pullRequests.totalCount) * 100;
+  if (include_merged_pull_requests) {
+    stats.totalPRsMerged = user.mergedPullRequests.totalCount;
+    stats.mergedPRsPercentage =
+      (user.mergedPullRequests.totalCount / user.pullRequests.totalCount) * 100;
+  }
   stats.totalReviews =
     user.contributionsCollection.totalPullRequestReviewContributions;
   stats.totalIssues = user.openIssues.totalCount + user.closedIssues.totalCount;
-  stats.totalDiscussionsStarted = user.repositoryDiscussions.totalCount;
-  stats.totalDiscussionsAnswered = user.repositoryDiscussionComments.totalCount;
+  if (include_discussions) {
+    stats.totalDiscussionsStarted = user.repositoryDiscussions.totalCount;
+  }
+  if (include_discussions_answers) {
+    stats.totalDiscussionsAnswered =
+      user.repositoryDiscussionComments.totalCount;
+  }
   stats.contributedTo = user.repositoriesContributedTo.totalCount;
 
   // Retrieve stars while filtering out repositories to be hidden.


Test patch:
diff --git a/tests/fetchStats.test.js b/tests/fetchStats.test.js
index 56125d8..ca8d7bc 100644
--- a/tests/fetchStats.test.js
+++ b/tests/fetchStats.test.js
@@ -122,12 +122,12 @@ describe("Test fetchStats", () => {
       totalCommits: 100,
       totalIssues: 200,
       totalPRs: 300,
-      totalPRsMerged: 240,
-      mergedPRsPercentage: 80,
+      totalPRsMerged: 0,
+      mergedPRsPercentage: 0,
       totalReviews: 50,
       totalStars: 300,
-      totalDiscussionsStarted: 10,
-      totalDiscussionsAnswered: 40,
+      totalDiscussionsStarted: 0,
+      totalDiscussionsAnswered: 0,
       rank,
     });
   });
@@ -158,12 +158,12 @@ describe("Test fetchStats", () => {
       totalCommits: 100,
       totalIssues: 200,
       totalPRs: 300,
-      totalPRsMerged: 240,
-      mergedPRsPercentage: 80,
+      totalPRsMerged: 0,
+      mergedPRsPercentage: 0,
       totalReviews: 50,
       totalStars: 300,
-      totalDiscussionsStarted: 10,
-      totalDiscussionsAnswered: 40,
+      totalDiscussionsStarted: 0,
+      totalDiscussionsAnswered: 0,
       rank,
     });
   });
@@ -200,12 +200,12 @@ describe("Test fetchStats", () => {
       totalCommits: 1000,
       totalIssues: 200,
       totalPRs: 300,
-      totalPRsMerged: 240,
-      mergedPRsPercentage: 80,
+      totalPRsMerged: 0,
+      mergedPRsPercentage: 0,
       totalReviews: 50,
       totalStars: 300,
-      totalDiscussionsStarted: 10,
-      totalDiscussionsAnswered: 40,
+      totalDiscussionsStarted: 0,
+      totalDiscussionsAnswered: 0,
       rank,
     });
   });
@@ -249,12 +249,12 @@ describe("Test fetchStats", () => {
       totalCommits: 1000,
       totalIssues: 200,
       totalPRs: 300,
-      totalPRsMerged: 240,
-      mergedPRsPercentage: 80,
+      totalPRsMerged: 0,
+      mergedPRsPercentage: 0,
       totalReviews: 50,
       totalStars: 200,
-      totalDiscussionsStarted: 10,
-      totalDiscussionsAnswered: 40,
+      totalDiscussionsStarted: 0,
+      totalDiscussionsAnswered: 0,
       rank,
     });
   });
@@ -280,12 +280,12 @@ describe("Test fetchStats", () => {
       totalCommits: 100,
       totalIssues: 200,
       totalPRs: 300,
-      totalPRsMerged: 240,
-      mergedPRsPercentage: 80,
+      totalPRsMerged: 0,
+      mergedPRsPercentage: 0,
       totalReviews: 50,
       totalStars: 400,
-      totalDiscussionsStarted: 10,
-      totalDiscussionsAnswered: 40,
+      totalDiscussionsStarted: 0,
+      totalDiscussionsAnswered: 0,
       rank,
     });
   });
@@ -311,12 +311,12 @@ describe("Test fetchStats", () => {
       totalCommits: 100,
       totalIssues: 200,
       totalPRs: 300,
-      totalPRsMerged: 240,
-      mergedPRsPercentage: 80,
+      totalPRsMerged: 0,
+      mergedPRsPercentage: 0,
       totalReviews: 50,
       totalStars: 300,
-      totalDiscussionsStarted: 10,
-      totalDiscussionsAnswered: 40,
+      totalDiscussionsStarted: 0,
+      totalDiscussionsAnswered: 0,
       rank,
     });
   });
@@ -336,6 +336,64 @@ describe("Test fetchStats", () => {
       followers: 100,
     });
 
+    expect(stats).toStrictEqual({
+      contributedTo: 61,
+      name: "Anurag Hazra",
+      totalCommits: 100,
+      totalIssues: 200,
+      totalPRs: 300,
+      totalPRsMerged: 0,
+      mergedPRsPercentage: 0,
+      totalReviews: 50,
+      totalStars: 300,
+      totalDiscussionsStarted: 0,
+      totalDiscussionsAnswered: 0,
+      rank,
+    });
+  });
+
+  it("should not fetch additional stats data when it not requested", async () => {
+    let stats = await fetchStats("anuraghazra");
+    const rank = calculateRank({
+      all_commits: false,
+      commits: 100,
+      prs: 300,
+      reviews: 50,
+      issues: 200,
+      repos: 5,
+      stars: 300,
+      followers: 100,
+    });
+
+    expect(stats).toStrictEqual({
+      contributedTo: 61,
+      name: "Anurag Hazra",
+      totalCommits: 100,
+      totalIssues: 200,
+      totalPRs: 300,
+      totalPRsMerged: 0,
+      mergedPRsPercentage: 0,
+      totalReviews: 50,
+      totalStars: 300,
+      totalDiscussionsStarted: 0,
+      totalDiscussionsAnswered: 0,
+      rank,
+    });
+  });
+
+  it("should fetch additional stats when it requested", async () => {
+    let stats = await fetchStats("anuraghazra", false, [], true, true, true);
+    const rank = calculateRank({
+      all_commits: false,
+      commits: 100,
+      prs: 300,
+      reviews: 50,
+      issues: 200,
+      repos: 5,
+      stars: 300,
+      followers: 100,
+    });
+
     expect(stats).toStrictEqual({
       contributedTo: 61,
       name: "Anurag Hazra",