@@ -9,58 +9,98 @@ type UserStat = {
99export const fetchTopUsersByPullRequests = async (
1010 repo : string
1111) : Promise < UserStat [ ] > => {
12- //* Limit to first 300 PRs only
13- let url = `https://api.github.com/repos/${ repo } /pulls?state=closed&per_page=100&page=1` ;
1412 const userStats : { [ key : string ] : { prCount : number ; avatarUrl : string } } = { } ;
15- let pageCount = 0 ;
16- const MAX_PAGES = 3 ;
13+ const MAX_CONCURRENT_REQUESTS = 3 ;
14+ const PER_PAGE = 100 ;
1715
18- while ( url && pageCount < MAX_PAGES ) {
19- const response = await axios . get ( url , {
20- timeout : 5000 ,
16+ try {
17+ // First, get the total PR count
18+ const initialResponse = await axios . get ( `https://api.github.com/repos/${ repo } /pulls` , {
19+ params : {
20+ state : 'closed' ,
21+ per_page : 1
22+ } ,
2123 headers : {
2224 Authorization : `token ${ process . env . GITHUB_TOKEN } ` ,
2325 }
2426 } ) ;
25- pageCount ++ ;
26- const prData = response . data ;
27- prData . forEach ( ( pr : any ) => {
28- if ( pr . merged_at ) {
29- const username = pr . user . login ;
3027
31- //* Ignore dependabot and nikohoffren PRs
32- if (
33- username === "dependabot" ||
34- username === "dependabot[bot]" ||
35- username === "nikohoffren"
36- ) {
37- return ;
38- }
28+ const linkHeader = initialResponse . headers . link ;
29+ const lastPageMatch = linkHeader ?. match ( / p a g e = ( \d + ) > ; r e l = " l a s t " / ) ;
30+ const totalPages = lastPageMatch ? parseInt ( lastPageMatch [ 1 ] ) : 1 ;
31+
32+ // Fetch PRs in batches
33+ for ( let i = 0 ; i < totalPages ; i += MAX_CONCURRENT_REQUESTS ) {
34+ const requests = [ ] ;
3935
40- const avatarUrl = pr . user . avatar_url ;
41- userStats [ username ] = {
42- prCount : ( userStats [ username ] ?. prCount || 0 ) + 1 ,
43- avatarUrl,
44- } ;
36+ // Create batch of concurrent requests
37+ for ( let j = 0 ; j < MAX_CONCURRENT_REQUESTS && ( i + j ) < totalPages ; j ++ ) {
38+ const pageNum = i + j + 1 ;
39+ requests . push (
40+ axios . get ( `https://api.github.com/repos/${ repo } /pulls` , {
41+ params : {
42+ state : 'closed' ,
43+ per_page : PER_PAGE ,
44+ page : pageNum
45+ } ,
46+ headers : {
47+ Authorization : `token ${ process . env . GITHUB_TOKEN } ` ,
48+ } ,
49+ timeout : 8000
50+ } ) . catch ( error => {
51+ console . error ( `Failed to fetch page ${ pageNum } :` , error ) ;
52+ return { data : [ ] } ;
53+ } )
54+ ) ;
4555 }
46- } ) ;
4756
48- const linkHeader = response . headers . link ;
49- const nextLink = linkHeader
50- ? linkHeader . split ( "," ) . find ( ( s : string ) => s . includes ( 'rel="next"' ) )
51- : null ;
52- url = nextLink ? nextLink . match ( / < ( .* ) > / ) ?. [ 1 ] : null ;
53- }
57+ // Wait for batch to complete
58+ const responses = await Promise . all ( requests ) ;
5459
55- const sortedUsers : UserStat [ ] = Object . entries ( userStats )
56- . sort ( ( [ , a ] , [ , b ] ) => b . prCount - a . prCount )
57- . slice ( 0 , 20 )
58- . map ( ( [ username , { prCount, avatarUrl } ] ) => ( {
59- username,
60- prCount,
61- avatarUrl,
62- } ) ) ;
60+ // Process the responses
61+ responses . forEach ( response => {
62+ if ( response . data ) {
63+ response . data . forEach ( ( pr : any ) => {
64+ if ( pr . merged_at ) {
65+ const username = pr . user . login ;
6366
64- return sortedUsers ;
65- } ;
67+ // Ignore specific users
68+ if (
69+ username === "dependabot" ||
70+ username === "dependabot[bot]" ||
71+ username === "nikohoffren"
72+ ) {
73+ return ;
74+ }
75+
76+ const avatarUrl = pr . user . avatar_url ;
77+ userStats [ username ] = {
78+ prCount : ( userStats [ username ] ?. prCount || 0 ) + 1 ,
79+ avatarUrl,
80+ } ;
81+ }
82+ } ) ;
83+ }
84+ } ) ;
85+
86+ // Add a small delay between batches to avoid rate limiting
87+ if ( i + MAX_CONCURRENT_REQUESTS < totalPages ) {
88+ await new Promise ( resolve => setTimeout ( resolve , 1000 ) ) ;
89+ }
90+ }
6691
92+ const sortedUsers : UserStat [ ] = Object . entries ( userStats )
93+ . sort ( ( [ , a ] , [ , b ] ) => b . prCount - a . prCount )
94+ . slice ( 0 , 20 )
95+ . map ( ( [ username , { prCount, avatarUrl } ] ) => ( {
96+ username,
97+ prCount,
98+ avatarUrl,
99+ } ) ) ;
100+
101+ return sortedUsers ;
102+ } catch ( error ) {
103+ console . error ( 'Error fetching PRs:' , error ) ;
104+ throw error ;
105+ }
106+ } ;
0 commit comments