Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 70 additions & 59 deletions src/lib/githubYearInReview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,47 @@ import "server-only";
import { GitHubApiError, RateLimitError, UserNotFoundError, type YearInReviewData } from "@/lib/types";
import { buildHourlyHeatmapFromCommitDates, getMostActiveDayFromCalendar, getMostActiveHour } from "@/lib/yearInReviewUtils";

const YEAR_IN_REVIEW_QUERY = `query($login: String!, $from: DateTime!, $to: DateTime!, $maxRepositories: Int!) {
user(login: $login) {
contributionsCollection(from: $from, to: $to) {
totalCommitContributions
totalPullRequestContributions
totalIssueContributions
totalPullRequestReviewContributions
contributionCalendar {
totalContributions
weeks {
contributionDays {
date
contributionCount
}
}
}
commitContributionsByRepository(maxRepositories: $maxRepositories) {
repository {
name
owner { login }
}
contributions { totalCount }
}
pullRequestContributionsByRepository(maxRepositories: $maxRepositories) {
repository {
name
owner { login }
}
contributions { totalCount }
}
issueContributionsByRepository(maxRepositories: $maxRepositories) {
repository {
name
owner { login }
}
contributions { totalCount }
}
}
}
}`;

const GITHUB_API = "https://api.github.com";
const GITHUB_GRAPHQL = "https://api.github.com/graphql";

Expand Down Expand Up @@ -159,6 +200,32 @@ async function fetchCommitDatesForTopRepos(
return results.flat();
}


function buildYearInReviewData(
year: number,
collection: NonNullable<YearInReviewResponse["user"]>["contributionsCollection"],
commitDates: string[]
): YearInReviewData {
const contributionCalendar = collection.contributionCalendar.weeks.flatMap((week) =>
week.contributionDays.map((day) => ({ date: day.date, count: day.contributionCount }))
);

const hourlyHeatmap = buildHourlyHeatmapFromCommitDates(commitDates);

return {
year,
totalContributions: collection.contributionCalendar.totalContributions,
totalCommits: collection.totalCommitContributions,
totalPRs: collection.totalPullRequestContributions,
totalIssues: collection.totalIssueContributions,
totalReviews: collection.totalPullRequestReviewContributions,
mostActiveDay: getMostActiveDayFromCalendar(contributionCalendar),
mostActiveHour: getMostActiveHour(hourlyHeatmap),
topRepository: mergeTopRepository(collection),
contributionCalendar,
};
}

export async function fetchYearInReviewData(username: string, year: number, token?: string): Promise<YearInReviewData> {
if (!token) {
throw new GitHubApiError("Year in Review requires authentication token", 401);
Expand All @@ -167,62 +234,19 @@ export async function fetchYearInReviewData(username: string, year: number, toke
const from = new Date(Date.UTC(year, 0, 1, 0, 0, 0));
const to = new Date(Date.UTC(year, 11, 31, 23, 59, 59));

const query = `query($login: String!, $from: DateTime!, $to: DateTime!) {
user(login: $login) {
contributionsCollection(from: $from, to: $to) {
totalCommitContributions
totalPullRequestContributions
totalIssueContributions
totalPullRequestReviewContributions
contributionCalendar {
totalContributions
weeks {
contributionDays {
date
contributionCount
}
}
}
commitContributionsByRepository(maxRepositories: 10) {
repository {
name
owner { login }
}
contributions { totalCount }
}
pullRequestContributionsByRepository(maxRepositories: 10) {
repository {
name
owner { login }
}
contributions { totalCount }
}
issueContributionsByRepository(maxRepositories: 10) {
repository {
name
owner { login }
}
contributions { totalCount }
}
}
}
}`;

const response = await graphql<YearInReviewResponse>(query, token, {
const response = await graphql<YearInReviewResponse>(YEAR_IN_REVIEW_QUERY, token, {
login: username,
from: from.toISOString(),
to: to.toISOString(),
maxRepositories: 10,
});

if (!response.user) {
throw new UserNotFoundError(username);
}

const collection = response.user.contributionsCollection;
const contributionCalendar = collection.contributionCalendar.weeks.flatMap((week) =>
week.contributionDays.map((day) => ({ date: day.date, count: day.contributionCount }))
);

const commitDates = await fetchCommitDatesForTopRepos(
username,
token,
Expand All @@ -231,20 +255,7 @@ export async function fetchYearInReviewData(username: string, year: number, toke
collection.commitContributionsByRepository
);

const hourlyHeatmap = buildHourlyHeatmapFromCommitDates(commitDates);

return {
year,
totalContributions: collection.contributionCalendar.totalContributions,
totalCommits: collection.totalCommitContributions,
totalPRs: collection.totalPullRequestContributions,
totalIssues: collection.totalIssueContributions,
totalReviews: collection.totalPullRequestReviewContributions,
mostActiveDay: getMostActiveDayFromCalendar(contributionCalendar),
mostActiveHour: getMostActiveHour(hourlyHeatmap),
topRepository: mergeTopRepository(collection),
contributionCalendar,
};
return buildYearInReviewData(year, collection, commitDates);
}

export async function fetchCommitActivityHeatmap(username: string, year: number, token?: string): Promise<number[][]> {
Expand Down
Loading