Skip to content
Open
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
143 changes: 1 addition & 142 deletions server/modules/github/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,145 +176,4 @@ class GitHubService {
const repoLanguages = await Promise.all(
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))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

node --check server/modules/github/service.js
rg -n 'static async getDashboard|static async syncDashboard|export default GitHubService;' \
  server/modules/github/service.js

Repository: kunalverma2512/CodeLens

Length of output: 421


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "## service.js end"
sed -n '130,190p' server/modules/github/service.js

echo
echo "## apexContextCompiler.js references"
rg -n 'langByBytes|GithubData|data\.langs|github' server/utils/apexContextCompiler.js

echo
echo "## full service symbols/length"
wc -l server/modules/github/service.js
rg -n 'class GitHubService|static async getDashboard|static async syncDashboard|export default GitHubService|topRepos|Promise\.all|langByBytes' server/modules/github/service.js

Repository: kunalverma2512/CodeLens

Length of output: 3554


🌐 Web query:

CodeLens kunalverma2512 server/modules/github/service.js repoLanguages Promise.all topRepos map langByBytes

💡 Result:

In the context of the CodeLens project by kunalverma2512 [1][2], the code pattern involving repoLanguages, Promise.all, topRepos, map, and langByBytes refers to a common approach for aggregating and visualizing programming language usage across a user's top GitHub repositories [3][4]. This implementation typically follows this workflow: 1. Fetching Top Repositories: The application first retrieves a list of a user's repositories (often filtered or sliced to identify the "top" repositories, e.g., the first 12) [3][4]. 2. Concurrent Data Fetching: It uses map to create an array of promises, each calling a service function (often named repoLanguages or similar) to fetch the language statistics for an individual repository via the GitHub API endpoint GET /repos/:owner/:repo/languages [3][4][5]. 3. Promise.all: The Promise.all function is used to execute these requests concurrently, significantly reducing the total time required to gather data for all repositories [3][4]. 4. Aggregating by Bytes: Once all language data is resolved, the code iterates through the resulting objects (which map language names to their byte count) to calculate a cumulative total of bytes per language across all selected repositories [3][4]. This often involves creating an aggregated or langByBytes object where language keys are updated by adding the byte counts returned by each repository [3][4]. This pattern is a standard architectural choice in dashboard applications to handle the latency of multiple sequential network requests to the GitHub API [3][4]. Similar implementations are found in various GitHub dashboard projects and technical discussions regarding language usage visualization [3][4][5].

Citations:


Restore the missing service body and fix the language-fetch promise chain.

server/modules/github/service.js exits at line 179 with a syntax error: .catch(...) is attached to the array returned by topRepos.map(...), and repoLanguages = await Promise.all(..., #fetchDashboardData, GitHubService, and export default GitHubService; are left unclosed. Move the error handling into each mapped fetch promise and restore the deleted aggregations, dashboard methods, class brace, and export.

🧰 Tools
🪛 Biome (2.5.5)

[error] 179-179: expected ) but instead the file ends

(parse)

🪛 GitHub Actions: Quality Gate / 0_Backend Lint & Test.txt

[error] 179-179: ESLint parsing error: Unexpected token. The 'npm run lint' command failed with exit code 1.

🪛 GitHub Actions: Quality Gate / Backend Lint & Test

[error] 179-179: ESLint parsing error: Unexpected token. The 'npm run lint' command failed with exit code 1.

🤖 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` at line 179, Repair GitHubService by moving
error handling into each promise created by topRepos.map, ensuring Promise.all
receives the mapped fetch promises rather than chaining .catch onto the
resulting array. Restore the repoLanguages aggregation, `#fetchDashboardData`
implementation, remaining dashboard methods, class closing brace, and export
default GitHubService so the service is syntactically complete.

Source: Linters/SAST tools

Loading