Skip to content

Commit 3675295

Browse files
committed
reducing api calls
1 parent de34841 commit 3675295

File tree

1 file changed

+92
-23
lines changed

1 file changed

+92
-23
lines changed

src/scripts/scrumHelper.js

Lines changed: 92 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ function allIncluded(outputTarget = 'email') {
3737
let issue_opened_button =
3838
'<div style="vertical-align:middle;display: inline-block;padding: 0px 4px;font-size:9px;font-weight: 600;color: #fff;text-align: center;background-color: #2cbe4e;border-radius: 3px;line-height: 12px;margin-bottom: 2px;" class="State State--green">open</div>';
3939

40-
const DEBUG = false; // Set to false to disable debug logs
40+
const DEBUG = true; // Set to false to disable debug logs
4141

4242
function log(...args) {
4343
if (DEBUG) {
@@ -319,34 +319,103 @@ function allIncluded(outputTarget = 'email') {
319319
const githubUserData = await userRes.json();
320320
const allPrsData = await allPrsRes.json();
321321

322-
// Fetch commits for each PR
323-
const prCommits = await Promise.all(allPrsData.items.map(async pr => {
324-
const repository_url = pr.repository_url;
325-
const [owner, project] = repository_url.split('/').slice(-2);
326-
const commitsUrl = `https://api.github.com/repos/${owner}/${project}/pulls/${pr.number}/commits`;
327-
328-
try {
329-
await new Promise(res => setTimeout(res, 100)); // Small delay to avoid rate limits
330-
const commitsRes = await fetch(commitsUrl);
331-
if (!commitsRes.ok) return null;
332-
const commits = await commitsRes.json();
322+
// Filter to only get open PRs
323+
const openPrs = allPrsData.items.filter(pr => pr.state === 'open');
324+
log(`Found ${openPrs.length} open PRs out of ${allPrsData.items.length} total PRs`);
325+
326+
// Fetch commits for each open PR with better rate limiting and caching
327+
const fetchCommitsWithRetry = async (owner, project, pr_number, retryCount = 3) => {
328+
const commitsUrl = `https://api.github.com/repos/${owner}/${project}/pulls/${pr_number}/commits`;
329+
330+
// Check if we have an error cached for this URL
331+
const errorKey = `${owner}/${project}/${pr_number}`;
332+
const cachedError = githubCache.errors[errorKey];
333+
if (cachedError && (Date.now() - cachedError.timestamp) < githubCache.errorTTL) {
334+
log(`Skipping ${errorKey} due to recent error`);
335+
return null;
336+
}
337+
338+
for (let i = 0; i < retryCount; i++) {
339+
try {
340+
// Add exponential backoff between retries
341+
if (i > 0) {
342+
await new Promise(res => setTimeout(res, Math.pow(2, i) * 1000));
343+
}
344+
345+
const commitsRes = await fetch(commitsUrl);
346+
if (commitsRes.status === 404) {
347+
// Cache 404 errors to avoid retrying
348+
githubCache.errors[errorKey] = { timestamp: Date.now(), status: 404 };
349+
return null;
350+
}
351+
352+
if (commitsRes.status === 403) {
353+
// Rate limit hit - wait longer
354+
const resetTime = commitsRes.headers.get('X-RateLimit-Reset');
355+
if (resetTime) {
356+
const waitTime = (parseInt(resetTime) * 1000) - Date.now();
357+
if (waitTime > 0) {
358+
await new Promise(res => setTimeout(res, waitTime));
359+
}
360+
}
361+
continue;
362+
}
363+
364+
if (!commitsRes.ok) {
365+
throw new Error(`HTTP ${commitsRes.status}`);
366+
}
367+
368+
const commits = await commitsRes.json();
369+
log(`Fetched ${commits.length} commits for PR #${pr_number} in ${owner}/${project}`);
370+
return commits;
371+
372+
} catch (err) {
373+
if (i === retryCount - 1) {
374+
// Cache error on final retry
375+
githubCache.errors[errorKey] = { timestamp: Date.now(), error: err.message };
376+
logError(`Failed to fetch commits for PR #${pr_number} after ${retryCount} retries:`, err);
377+
return null;
378+
}
379+
}
380+
}
381+
return null;
382+
};
383+
384+
// Process PRs in batches to avoid rate limiting
385+
const batchSize = 3;
386+
const prCommits = [];
387+
388+
for (let i = 0; i < openPrs.length; i += batchSize) {
389+
const batch = openPrs.slice(i, i + batchSize);
390+
const batchResults = await Promise.all(batch.map(async pr => {
391+
const repository_url = pr.repository_url;
392+
const [owner, project] = repository_url.split('/').slice(-2);
393+
394+
// Add delay between PR commit fetches
395+
await new Promise(res => setTimeout(res, 1000));
396+
397+
const commits = await fetchCommitsWithRetry(owner, project, pr.number);
398+
if (!commits) return null;
399+
400+
const filteredCommits = commits.filter(commit =>
401+
commit.author?.login === githubUsername &&
402+
new Date(commit.commit.author.date) >= new Date(startingDate) &&
403+
new Date(commit.commit.author.date) <= new Date(endingDate)
404+
);
405+
406+
if (filteredCommits.length === 0) return null;
407+
333408
return {
334409
pr,
335-
commits: commits.filter(commit =>
336-
commit.author?.login === githubUsername &&
337-
new Date(commit.commit.author.date) >= new Date(startingDate) &&
338-
new Date(commit.commit.author.date) <= new Date(endingDate)
339-
)
410+
commits: filteredCommits
340411
};
341-
} catch (err) {
342-
console.error(`Error fetching commits for PR #${pr.number}:`, err);
343-
return null;
344-
}
345-
}));
412+
}));
413+
414+
prCommits.push(...batchResults.filter(Boolean));
415+
}
346416

347417
// Filter out null results and empty commits
348418
const prCommitsData = prCommits
349-
.filter(data => data && data.commits.length > 0)
350419
.reduce((acc, { pr, commits }) => {
351420
const project = pr.repository_url.split('/').pop();
352421
if (!acc[project]) acc[project] = [];

0 commit comments

Comments
 (0)