Skip to content

Commit

Permalink
feat: add include_orgs card variable
Browse files Browse the repository at this point in the history
This pull request adds experimental Organization support. Users can
include stats from Organization repositories to which they are
collaborators using the `include_orgs` card argument. Please be aware
that because of #1852, only the first 100 repositories are used.
Including organization, stats might therefore skew your results.

Co-authored-by: Raymond Nook <[email protected]>
  • Loading branch information
rickstaa and developStorm committed Nov 21, 2022
1 parent 3cb205c commit 5fb41f7
Show file tree
Hide file tree
Showing 8 changed files with 47 additions and 16 deletions.
2 changes: 2 additions & 0 deletions api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default async (req, res) => {
show_icons,
count_private,
include_all_commits,
include_orgs,
line_height,
title_color,
ring_color,
Expand Down Expand Up @@ -52,6 +53,7 @@ export default async (req, res) => {
username,
parseBoolean(count_private),
parseBoolean(include_all_commits),
parseBoolean(include_orgs),
parseArray(exclude_repo),
);

Expand Down
2 changes: 2 additions & 0 deletions api/top-langs.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export default async (req, res) => {
cache_seconds,
layout,
langs_count,
include_orgs,
exclude_repo,
custom_title,
locale,
Expand All @@ -43,6 +44,7 @@ export default async (req, res) => {
try {
const topLangs = await fetchTopLanguages(
username,
include_orgs,
parseArray(exclude_repo),
);

Expand Down
2 changes: 2 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ You can provide multiple comma-separated values in the bg_color option to render
- `hide_rank` - _(boolean)_ hides the rank and automatically resizes the card width. Default: `false`.
- `show_icons` - _(boolean)_. Default: `false`.
- `include_all_commits` - Count total commits instead of just the current year commits _(boolean)_. Default: `false`.
- `include_orgs` - Include stats from organization repositories. Default: `false`.
- `count_private` - Count private commits _(boolean)_. Default: `false`.
- `line_height` - Sets the line height between text _(number)_. Default: `25`.
- `exclude_repo` - Exclude stars from specified repositories _(Comma-separated values)_. Default: `[] (blank array)`.
Expand All @@ -302,6 +303,7 @@ You can provide multiple comma-separated values in the bg_color option to render
- `layout` - Switch between two available layouts `default` & `compact`. Default: `default`.
- `card_width` - Set the card's width manually _(number)_. Default `300`.
- `langs_count` - Show more languages on the card, between 1-10 _(number)_. Default `5`.
- `include_orgs` - Include language stats from organization repositories. Default: `false`.
- `exclude_repo` - Exclude specified repositories _(Comma-separated values)_. Default: `[] (blank array)`.
- `custom_title` - Sets a custom title for the card _(string)_. Default `Most Used Languages`.

Expand Down
2 changes: 1 addition & 1 deletion src/common/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ const isValidHexColor = (hexColor) => {
/**
* Returns boolean if value is either "true" or "false" else the value as it is.
*
* @param {string | boolean} value The value to parse.
* @param {string | boolean| undefined} value The value to parse.
* @returns {boolean | undefined } The parsed value.
*/
const parseBoolean = (value) => {
Expand Down
31 changes: 23 additions & 8 deletions src/fetchers/stats-fetcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const fetcher = (variables, token) => {
return request(
{
query: `
query userInfo($login: String!) {
query userInfo($login: String!, $ownerAffiliations: [RepositoryAffiliation]) {
user(login: $login) {
name
login
Expand All @@ -45,7 +45,7 @@ const fetcher = (variables, token) => {
followers {
totalCount
}
repositories(ownerAffiliations: OWNER) {
repositories(ownerAffiliations: $ownerAffiliations) {
totalCount
}
}
Expand All @@ -70,9 +70,9 @@ const repositoriesFetcher = (variables, token) => {
return request(
{
query: `
query userInfo($login: String!, $after: String) {
query userInfo($login: String!, $after: String, $ownerAffiliations: [RepositoryAffiliation]) {
user(login: $login) {
repositories(first: 100, ownerAffiliations: OWNER, orderBy: {direction: DESC, field: STARGAZERS}, after: $after) {
repositories(first: 100, ownerAffiliations: $ownerAffiliations, orderBy: {direction: DESC, field: STARGAZERS}, after: $after) {
nodes {
name
stargazers {
Expand Down Expand Up @@ -141,15 +141,21 @@ const totalCommitsFetcher = async (username) => {
* Fetch all the stars for all the repositories of a given username.
*
* @param {string} username GitHub username.
* @param {boolean} include_orgs Include stats from organization repos.
* @param {array} repoToHide Repositories to hide.
* @returns {Promise<number>} Total stars.
*/
const totalStarsFetcher = async (username, repoToHide) => {
const totalStarsFetcher = async (username, include_orgs, repoToHide) => {
let nodes = [];
let hasNextPage = true;
let endCursor = null;
while (hasNextPage) {
const variables = { login: username, first: 100, after: endCursor };
const variables = {
login: username,
first: 100,
after: endCursor,
ownerAffiliations: include_orgs ? ["OWNER", "COLLABORATOR"] : ["OWNER"],
};
let res = await retryer(repositoriesFetcher, variables);

if (res.data.errors) {
Expand Down Expand Up @@ -183,12 +189,14 @@ const totalStarsFetcher = async (username, repoToHide) => {
* @param {string} username GitHub username.
* @param {boolean} count_private Include private contributions.
* @param {boolean} include_all_commits Include all commits.
* @param {boolean} include_orgs Include stats from organization repos.
* @returns {Promise<import("./types").StatsData>} Stats data.
*/
const fetchStats = async (
username,
count_private = false,
include_all_commits = false,
include_orgs = false,
exclude_repo = [],
) => {
if (!username) throw new MissingParamError(["username"]);
Expand All @@ -203,7 +211,10 @@ const fetchStats = async (
rank: { level: "C", score: 0 },
};

let res = await retryer(fetcher, { login: username });
let res = await retryer(fetcher, {
login: username,
ownerAffiliations: include_orgs ? ["OWNER", "COLLABORATOR"] : ["OWNER"],
});

// Catch GraphQL errors.
if (res.data.errors) {
Expand Down Expand Up @@ -259,7 +270,11 @@ const fetchStats = async (
stats.contributedTo = user.repositoriesContributedTo.totalCount;

// Retrieve stars while filtering out repositories to be hidden
stats.totalStars = await totalStarsFetcher(username, repoToHide);
stats.totalStars = await totalStarsFetcher(
username,
include_orgs,
repoToHide,
);

stats.rank = calculateRank({
totalCommits: stats.totalCommits,
Expand Down
16 changes: 12 additions & 4 deletions src/fetchers/top-languages-fetcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ const fetcher = (variables, token) => {
return request(
{
query: `
query userInfo($login: String!) {
query userInfo($login: String!, $ownerAffiliations: [RepositoryAffiliation]) {
user(login: $login) {
# fetch only owner repos & not forks
repositories(ownerAffiliations: OWNER, isFork: false, first: 100) {
repositories(ownerAffiliations: $ownerAffiliations, isFork: false, first: 100) {
nodes {
name
languages(first: 10, orderBy: {field: SIZE, direction: DESC}) {
Expand Down Expand Up @@ -51,13 +51,21 @@ const fetcher = (variables, token) => {
* Fetch top languages for a given username.
*
* @param {string} username GitHub username.
* @param {boolean} include_orgs Include stats from organization repos.
* @param {string[]} exclude_repo List of repositories to exclude.
* @returns {Promise<import("./types").TopLangData>} Top languages data.
*/
const fetchTopLanguages = async (username, exclude_repo = []) => {
const fetchTopLanguages = async (
username,
include_orgs = false,
exclude_repo = [],
) => {
if (!username) throw new MissingParamError(["username"]);

const res = await retryer(fetcher, { login: username });
const res = await retryer(fetcher, {
login: username,
ownerAffiliations: include_orgs ? ["OWNER", "COLLABORATOR"] : ["OWNER"],
});

if (res.data.errors) {
logger.error(res.data.errors);
Expand Down
6 changes: 4 additions & 2 deletions tests/fetchStats.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ describe("Test fetchStats", () => {
.onGet("https://api.github.com/search/commits?q=author:anuraghazra")
.reply(200, { total_count: 1000 });

let stats = await fetchStats("anuraghazra", true, true);
let stats = await fetchStats("anuraghazra", true, true, false);
const rank = calculateRank({
totalCommits: 1050,
totalRepos: 5,
Expand Down Expand Up @@ -230,7 +230,9 @@ describe("Test fetchStats", () => {
.onGet("https://api.github.com/search/commits?q=author:anuraghazra")
.reply(200, { total_count: 1000 });

let stats = await fetchStats("anuraghazra", true, true, ["test-repo-1"]);
let stats = await fetchStats("anuraghazra", true, true, false, [
"test-repo-1",
]);
const rank = calculateRank({
totalCommits: 1050,
totalRepos: 5,
Expand Down
2 changes: 1 addition & 1 deletion tests/fetchTopLanguages.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ describe("FetchTopLanguages", () => {
it("should fetch correct language data while excluding the 'test-repo-1' repository", async () => {
mock.onPost("https://api.github.com/graphql").reply(200, data_langs);

let repo = await fetchTopLanguages("anuraghazra", ["test-repo-1"]);
let repo = await fetchTopLanguages("anuraghazra", false, ["test-repo-1"]);
expect(repo).toStrictEqual({
HTML: {
color: "#0f0",
Expand Down

0 comments on commit 5fb41f7

Please sign in to comment.