@@ -10,86 +10,55 @@ export const fetchTopUsersByPullRequests = async (
1010 repo : string
1111) : Promise < UserStat [ ] > => {
1212 const userStats : { [ key : string ] : { prCount : number ; avatarUrl : string } } = { } ;
13- const MAX_CONCURRENT_REQUESTS = 3 ;
1413 const PER_PAGE = 100 ;
1514
1615 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- } ,
23- headers : {
24- Authorization : `token ${ process . env . GITHUB_TOKEN } ` ,
25- }
26- } ) ;
27-
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 = [ ] ;
35-
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- ) ;
55- }
16+ // Use cursor-based pagination instead of page numbers
17+ let hasNextPage = true ;
18+ let cursor = null ;
5619
57- // Wait for batch to complete
58- const responses = await Promise . all ( requests ) ;
59-
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 ;
20+ while ( hasNextPage ) {
21+ const response : { data : any [ ] ; headers : { link ?: string } } = await axios . get ( `https://api.github.com/repos/${ repo } /pulls` , {
22+ params : {
23+ state : 'closed' ,
24+ per_page : PER_PAGE ,
25+ ...( cursor ? { after : cursor } : { } ) ,
26+ } ,
27+ headers : {
28+ Authorization : `token ${ process . env . GITHUB_TOKEN } ` ,
29+ Accept : 'application/vnd.github.v3+json' ,
30+ } ,
31+ timeout : 30000 , // 30 seconds timeout
32+ } ) ;
6633
67- // Ignore specific users
68- if (
69- username === "dependabot" ||
70- username === "dependabot[bot]" ||
71- username === "nikohoffren"
72- ) {
73- return ;
74- }
34+ const prs = response . data ;
7535
76- const avatarUrl = pr . user . avatar_url ;
77- userStats [ username ] = {
78- prCount : ( userStats [ username ] ?. prCount || 0 ) + 1 ,
79- avatarUrl,
80- } ;
81- }
82- } ) ;
36+ // Process PRs
37+ prs . forEach ( ( pr : any ) => {
38+ if ( pr . merged_at ) {
39+ const username = pr . user . login ;
40+ if ( ! shouldExcludeUser ( username ) ) {
41+ userStats [ username ] = {
42+ prCount : ( userStats [ username ] ?. prCount || 0 ) + 1 ,
43+ avatarUrl : pr . user . avatar_url ,
44+ } ;
45+ }
8346 }
8447 } ) ;
8548
86- // Add a small delay between batches to avoid rate limiting
87- if ( i + MAX_CONCURRENT_REQUESTS < totalPages ) {
49+ // Check if there are more pages
50+ const linkHeader = response . headers . link ;
51+ hasNextPage = linkHeader ?. includes ( 'rel="next"' ) ?? false ;
52+ const nextLink = linkHeader ?. match ( / < ( [ ^ > ] + ) > ; \s * r e l = " n e x t " / ) ?. [ 1 ] ;
53+ cursor = nextLink ? new URL ( nextLink ) . searchParams . get ( 'after' ) : null ;
54+
55+ // Add delay between requests to avoid rate limiting
56+ if ( hasNextPage ) {
8857 await new Promise ( resolve => setTimeout ( resolve , 1000 ) ) ;
8958 }
9059 }
9160
92- const sortedUsers : UserStat [ ] = Object . entries ( userStats )
61+ return Object . entries ( userStats )
9362 . sort ( ( [ , a ] , [ , b ] ) => b . prCount - a . prCount )
9463 . slice ( 0 , 20 )
9564 . map ( ( [ username , { prCount, avatarUrl } ] ) => ( {
@@ -98,9 +67,14 @@ export const fetchTopUsersByPullRequests = async (
9867 avatarUrl,
9968 } ) ) ;
10069
101- return sortedUsers ;
10270 } catch ( error ) {
10371 console . error ( 'Error fetching PRs:' , error ) ;
10472 throw error ;
10573 }
10674} ;
75+
76+ function shouldExcludeUser ( username : string ) : boolean {
77+ const excludedUsers = [ 'dependabot' , 'dependabot[bot]' , 'nikohoffren' ] ;
78+ return excludedUsers . includes ( username ) ;
79+ }
80+
0 commit comments