Skip to content
Open
Show file tree
Hide file tree
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
29 changes: 25 additions & 4 deletions src/services/codechef.service.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { githubCache } from '../utils/cache.js';
import { HttpErrorCode, httpRequest } from '../utils/http-client.js';

function getDivision(rating) {
Expand All @@ -8,6 +9,21 @@ function getDivision(rating) {
}

export async function getCodeChefData(handle) {
if (!handle) {
return {
success: false,
error: 'CodeChef handle not provided',
};
}

const cacheKey = `codechef:${handle}`;

// Check cache first
const cached = githubCache.get(cacheKey);
if (cached) {
return cached;
}

try {
const safeHandle = encodeURIComponent(handle);

Expand All @@ -27,7 +43,7 @@ export async function getCodeChefData(handle) {

const currentRating = data.rating_number ?? 0;

return {
const result = {
success: true,
data: {
handle: data.username ?? handle,
Expand All @@ -36,9 +52,14 @@ export async function getCodeChefData(handle) {
globalRank: data.globalRank ?? data.global_rank ?? 'N/A',
problemsSolved: data.problemsSolved ?? data.problems_solved ?? 0,
division: getDivision(currentRating),
}
},
};

// Store successful result in cache
githubCache.set(cacheKey, result);

return result;
} catch (err) {
return { success: false, error: "CodeChef API error" };
return { success: false, error: 'CodeChef API error' };
}
}
}
29 changes: 25 additions & 4 deletions src/services/codeforces.service.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
import { githubCache } from '../utils/cache.js';
import { HttpErrorCode, httpRequest } from '../utils/http-client.js';

export async function getCodeforcesData(handle) {
if (!handle) {
return {
success: false,
error: 'Codeforces handle not provided',
};
}

const cacheKey = `codeforces:${handle}`;

// Check cache first
const cached = githubCache.get(cacheKey);
if (cached) {
return cached;
}

try {
const safeHandle = encodeURIComponent(handle);

Expand Down Expand Up @@ -36,7 +52,7 @@ export async function getCodeforcesData(handle) {
problemsSolved = solved.size;
}

return {
const result = {
success: true,
data: {
handle: user.handle,
Expand All @@ -45,9 +61,14 @@ export async function getCodeforcesData(handle) {
rank: user.rank ?? 'unrated',
maxRank: user.maxRank ?? 'unrated',
problemsSolved,
}
},
};

// Store successful result in cache
githubCache.set(cacheKey, result);

return result;
} catch (err) {
return { success: false, error: "Codeforces API error" };
return { success: false, error: 'Codeforces API error' };
}
}
}
36 changes: 15 additions & 21 deletions src/services/github.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,32 +93,26 @@ async function fetchUserProfile(username) {

/* fetch public repos for a user */
async function fetchUserRepos(username) {
const repos = [];
let page = 1;
const perPage = 100;

// Fetch first page
const response1 = await httpRequest(
`${GITHUB_API_BASE}/users/${username}/repos?per_page=${perPage}&page=1&sort=updated`,
{ headers: getHeaders() }
);
await assertOk(response1);
const repos = response1.data || [];

// If first page has exactly 100 items, fetch pages 2 and 3 in parallel
if (repos.length === perPage) {
const promises = [2, 3].map(page =>
httpRequest(
`${GITHUB_API_BASE}/users/${username}/repos?per_page=${perPage}&page=${page}&sort=updated`,
{ headers: getHeaders() }
)
while (true) {
const response = await httpRequest(
`${GITHUB_API_BASE}/users/${username}/repos?per_page=${perPage}&page=${page}&sort=updated`,
{ headers: getHeaders() }
);

const responses = await Promise.all(promises);
for (const res of responses) {
await assertOk(res);
if (res.data && res.data.length > 0) {
repos.push(...res.data);
}
await assertOk(response);

const data = Array.isArray(response.data) ? response.data : [];
repos.push(...data);

if (data.length < perPage) {
break;
}

page++;
}

return repos;
Expand Down
6 changes: 5 additions & 1 deletion src/services/tests/codechef.service.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { afterEach, describe, expect, jest, test } from '@jest/globals';
import { afterEach, beforeEach, describe, expect, jest, test } from '@jest/globals';
import { githubCache } from '../../utils/cache.js';
import { getCodeChefData } from '../codechef.service.js';

// ---------------------------------------------------------------------------
Expand All @@ -23,6 +24,9 @@ function jsonResponse(data, init = {}) {
// ---------------------------------------------------------------------------

describe('codechef.service.js', () => {
beforeEach(() => {
githubCache.clear();
});
afterEach(() => {
delete globalThis.fetch;
});
Expand Down
6 changes: 5 additions & 1 deletion src/services/tests/codeforces.service.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { afterEach, describe, expect, jest, test } from '@jest/globals';
import { afterEach, beforeEach, describe, expect, jest, test } from '@jest/globals';
import { githubCache } from '../../utils/cache.js';
import { getCodeforcesData } from '../codeforces.service.js';

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -31,6 +32,9 @@ function errorResponse(status) {
// ---------------------------------------------------------------------------

describe('codeforces.service.js', () => {
beforeEach(() => {
githubCache.clear();
});
afterEach(() => {
delete globalThis.fetch;
});
Expand Down