fix: resolve 1 bugs - #315
Conversation
|
Someone is attempting to deploy a commit to the Kunal Verma's projects Team on Vercel. A member of the Team first needs to authorize it. |
📝 WalkthroughWalkthroughThe GitHub language-fetching path now handles nullish ChangesGitHub language-fetching resilience
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 Biome (2.5.5)server/modules/github/service.jsFile contains syntax errors that prevent linting: Line 179: expected Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🎉 Welcome to CodeLens — Thank You for Your Contribution!Hey @saurabhhhcodes! 👋 We are genuinely excited to have you here. Every single PR — big or small — makes CodeLens better, and yours is no exception. Take a moment to review the checklist below to help us merge your work quickly and smoothly. ✅ Before Requesting a Review
💬 Join Our Community Channel — This is MandatoryBeing part of our communication channel is compulsory for all contributors, not optional. Why join? This is where all important announcements, PR review updates, contribution discussions, and maintainer decisions happen in real time. Contributors who are not in the channel regularly miss critical context and updates, which often leads to duplicated or misaligned work. Staying connected here is what keeps the community strong and your contributions impactful. We are rooting for you! If you have any questions, drop them in the channel or comment right here on this PR. Let's build something great together. 🚀✨ |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@server/modules/github/service.js`:
- Around line 177-179: Update the mapped promise in the repository-language
fetch expression so each ghFetch call has its own catch handler before the map
closes; log the error and return an empty language result for that repository.
Then close the surrounding map/Promise.all expression correctly, preserving
stable results when individual requests reject.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 67d7054c-1b5c-4367-ab50-58c2d63b3211
📒 Files selected for processing (1)
server/modules/github/service.js
| (topRepos ?? []).map(r => ghFetch(`${GH_API}/repos/${r.full_name}/languages`, token) | ||
| .then(langs => ({ repo: r.name, langs: langs || {} }))) | ||
| ); | ||
|
|
||
| // Aggregate bytes across all repos | ||
| const langBytes = {}; | ||
| repoLanguages.forEach(({ langs }) => { | ||
| Object.entries(langs).forEach(([lang, bytes]) => { | ||
| langBytes[lang] = (langBytes[lang] || 0) + bytes; | ||
| }); | ||
| }); | ||
| const langByBytes = Object.entries(langBytes) | ||
| .sort((a, b) => b[1] - a[1]) | ||
| .slice(0, 10) | ||
| .map(([name, bytes]) => ({ name, bytes })); | ||
|
|
||
| // Repo stats | ||
| const ownedRepos = (repos || []).filter(r => !r.fork); | ||
| const forkedRepos = (repos || []).filter(r => r.fork); | ||
| const totalStars = (repos || []).reduce((s, r) => s + r.stargazers_count, 0); | ||
| const totalForks = (repos || []).reduce((s, r) => s + r.forks_count, 0); | ||
| const topByStars = [...(repos || [])].sort((a, b) => b.stargazers_count - a.stargazers_count).slice(0, 10); | ||
| const recentlyPushed = [...(repos || [])].sort((a, b) => new Date(b.pushed_at) - new Date(a.pushed_at)).slice(0, 5); | ||
|
|
||
| // Starred repo topics analysis | ||
| const starredTopics = {}; | ||
| const starredLangs = {}; | ||
| (starred || []).forEach(r => { | ||
| if (r.language) starredLangs[r.language] = (starredLangs[r.language] || 0) + 1; | ||
| (r.topics || []).forEach(t => { starredTopics[t] = (starredTopics[t] || 0) + 1; }); | ||
| }); | ||
|
|
||
| // Compute derived metrics | ||
| const metrics = this.#computeMetrics({ repos, contributions, events, prs, issues }); | ||
|
|
||
| console.log(`[GitHub] ✓ Complete`); | ||
|
|
||
| return { | ||
| profile, | ||
| orgs: orgs || [], | ||
| repos: repos || [], | ||
| ownedRepos, | ||
| forkedRepos, | ||
| topByStars, | ||
| recentlyPushed, | ||
| totalStars, | ||
| totalForks, | ||
| langByBytes, | ||
| repoLanguages, | ||
| gists: gists || [], | ||
| starred: starred || [], | ||
| starredTopics: Object.entries(starredTopics).sort((a, b) => b[1] - a[1]).slice(0, 12).map(([t, c]) => ({ topic: t, count: c })), | ||
| starredLangs: Object.entries(starredLangs).sort((a, b) => b[1] - a[1]).slice(0, 8).map(([name, count]) => ({ name, count })), | ||
| events: events || [], | ||
| contributions, | ||
| prs: prs || { total_count: 0, items: [] }, | ||
| issues: issues || { total_count: 0, items: [] }, | ||
| metrics, | ||
| }; | ||
| } | ||
|
|
||
| /** ── Full dashboard (cached) ────────────────────────────────────────── */ | ||
| static async getDashboard(userId) { | ||
| // Check if we have cached data for this user | ||
| let githubData = await GithubData.findOne({ userId }); | ||
|
|
||
| if (githubData && githubData.data) { | ||
| console.log(`[GitHub] ✓ Dashboard returned from cache for user ${userId}`); | ||
| return { | ||
| ...githubData.data, | ||
| lastSyncedAt: githubData.lastSyncedAt | ||
| }; | ||
| } | ||
|
|
||
| // If no cache, fetch fresh data and store it | ||
| const { token, username } = await this.#getToken(userId); | ||
| const data = await this.#fetchDashboardData(userId, token, username); | ||
|
|
||
| await GithubData.findOneAndUpdate( | ||
| { userId }, | ||
| { userId, data, lastSyncedAt: new Date() }, | ||
| { upsert: true, new: true } | ||
| ); | ||
|
|
||
| return { | ||
| ...data, | ||
| lastSyncedAt: new Date() | ||
| }; | ||
| } | ||
|
|
||
| /** ── Manual Sync Dashboard ─────────────────────────────────────────── */ | ||
| static async syncDashboard(userId) { | ||
| const { token, username } = await this.#getToken(userId); | ||
| const data = await this.#fetchDashboardData(userId, token, username); | ||
|
|
||
| const githubData = await GithubData.findOneAndUpdate( | ||
| { userId }, | ||
| { userId, data, lastSyncedAt: new Date() }, | ||
| { upsert: true, new: true } | ||
| ); | ||
|
|
||
| return { | ||
| ...data, | ||
| lastSyncedAt: githubData.lastSyncedAt | ||
| }; | ||
| } | ||
|
|
||
| static async getProfile(userId) { | ||
| const { token } = await this.#getToken(userId); | ||
| const [profile, orgs] = await Promise.all([ | ||
| ghFetch(`${GH_API}/user`, token), | ||
| ghFetch(`${GH_API}/user/orgs`, token, { per_page: 100 }), | ||
| ]); | ||
| return { profile, orgs: orgs || [] }; | ||
| } | ||
|
|
||
| static async getRepositories(userId) { | ||
| const { token } = await this.#getToken(userId); | ||
| const repos = await ghFetch(`${GH_API}/user/repos`, token, { per_page: 100, sort: "updated", type: "owner" }); | ||
| const totalStars = (repos || []).reduce((s, r) => s + r.stargazers_count, 0); | ||
| const totalForks = (repos || []).reduce((s, r) => s + r.forks_count, 0); | ||
| const langs = {}; | ||
| (repos || []).forEach(r => { if (r.language) langs[r.language] = (langs[r.language] || 0) + 1; }); | ||
| return { | ||
| repos: repos || [], | ||
| languages: Object.entries(langs).sort((a, b) => b[1] - a[1]).map(([name, count]) => ({ name, count })), | ||
| topByStars: [...(repos || [])].sort((a, b) => b.stargazers_count - a.stargazers_count).slice(0, 10), | ||
| totalStars, totalForks, | ||
| }; | ||
| } | ||
|
|
||
| static async getContributions(userId) { | ||
| const { token, username } = await this.#getToken(userId); | ||
| return this.#fetchContributions(username, token); | ||
| } | ||
|
|
||
| static async getActivity(userId) { | ||
| const { token, username } = await this.#getToken(userId); | ||
| const events = await ghFetch(`${GH_API}/users/${username}/events`, token, { per_page: 100 }); | ||
| return events || []; | ||
| } | ||
| } | ||
|
|
||
| export default GitHubService; | ||
| .catch(err => console.error(err)) No newline at end of file |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
Keep .catch() inside each mapped promise and close the outer expression.
Line 178 closes map() before .catch() executes. Line 179 therefore cannot catch a ghFetch() rejection for one repository. The current parentheses also leave the outer expression unclosed, so the file does not parse.
Return an empty language result after logging. This keeps the Promise.all() result stable when one request fails.
Proposed fix
- (topRepos ?? []).map(r => ghFetch(`${GH_API}/repos/${r.full_name}/languages`, token)
- .then(langs => ({ repo: r.name, langs: langs || {} })))
- .catch(err => console.error(err))
+ (topRepos ?? []).map(r => ghFetch(`${GH_API}/repos/${r.full_name}/languages`, token)
+ .then(langs => ({ repo: r.name, langs: langs || {} }))
+ .catch(err => {
+ console.error(err)
+ return { repo: r.name, langs: {} }
+ }))🧰 Tools
🪛 Biome (2.5.5)
[error] 179-179: expected ) but instead the file ends
(parse)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@server/modules/github/service.js` around lines 177 - 179, Update the mapped
promise in the repository-language fetch expression so each ghFetch call has its
own catch handler before the map closes; log the error and return an empty
language result for that repository. Then close the surrounding map/Promise.all
expression correctly, preserving stable results when individual requests reject.
Source: Linters/SAST tools
Description
This PR fixes real bugs found in the codebase:
.map()on an undefined collection threwTypeError; now falls back to[].Type of Change
How Has This Been Tested?
Checklist
Related Issue
Ref: #333
Summary by CodeRabbit