@@ -112,7 +112,7 @@ type CalculateResponse struct {
112112type RepoSampleRequest struct {
113113 Owner string `json:"owner"`
114114 Repo string `json:"repo"`
115- SampleSize int `json:"sample_size,omitempty"` // Default: 25
115+ SampleSize int `json:"sample_size,omitempty"` // Default: 30
116116 Days int `json:"days,omitempty"` // Default: 90
117117 Config * cost.Config `json:"config,omitempty"`
118118}
@@ -122,7 +122,7 @@ type RepoSampleRequest struct {
122122//nolint:govet // fieldalignment: API struct field order optimized for readability
123123type OrgSampleRequest struct {
124124 Org string `json:"org"`
125- SampleSize int `json:"sample_size,omitempty"` // Default: 25
125+ SampleSize int `json:"sample_size,omitempty"` // Default: 30
126126 Days int `json:"days,omitempty"` // Default: 90
127127 Config * cost.Config `json:"config,omitempty"`
128128}
@@ -1150,18 +1150,18 @@ func (s *Server) parseRepoSampleRequest(ctx context.Context, r *http.Request) (*
11501150
11511151 // Set defaults
11521152 if req .SampleSize == 0 {
1153- req .SampleSize = 25
1153+ req .SampleSize = 30
11541154 }
11551155 if req .Days == 0 {
11561156 req .Days = 90
11571157 }
11581158
1159- // Validate reasonable limits (silently cap at 25 )
1159+ // Validate reasonable limits (silently cap at 50 )
11601160 if req .SampleSize < 1 {
11611161 return nil , errors .New ("sample_size must be at least 1" )
11621162 }
1163- if req .SampleSize > 25 {
1164- req .SampleSize = 25
1163+ if req .SampleSize > 50 {
1164+ req .SampleSize = 50
11651165 }
11661166 if req .Days < 1 || req .Days > 365 {
11671167 return nil , errors .New ("days must be between 1 and 365" )
@@ -1208,18 +1208,18 @@ func (s *Server) parseOrgSampleRequest(ctx context.Context, r *http.Request) (*O
12081208
12091209 // Set defaults
12101210 if req .SampleSize == 0 {
1211- req .SampleSize = 25
1211+ req .SampleSize = 30
12121212 }
12131213 if req .Days == 0 {
12141214 req .Days = 90
12151215 }
12161216
1217- // Validate reasonable limits (silently cap at 25 )
1217+ // Validate reasonable limits (silently cap at 50 )
12181218 if req .SampleSize < 1 {
12191219 return nil , errors .New ("sample_size must be at least 1" )
12201220 }
1221- if req .SampleSize > 25 {
1222- req .SampleSize = 25
1221+ if req .SampleSize > 50 {
1222+ req .SampleSize = 50
12231223 }
12241224 if req .Days < 1 || req .Days > 365 {
12251225 return nil , errors .New ("days must be between 1 and 365" )
@@ -1249,7 +1249,7 @@ func (s *Server) processRepoSample(ctx context.Context, req *RepoSampleRequest,
12491249 } else {
12501250 // Fetch all PRs modified since the date
12511251 var err error
1252- prs , err = github .FetchPRsFromRepo (ctx , req .Owner , req .Repo , since , token )
1252+ prs , err = github .FetchPRsFromRepo (ctx , req .Owner , req .Repo , since , token , nil )
12531253 if err != nil {
12541254 return nil , fmt .Errorf ("failed to fetch PRs: %w" , err )
12551255 }
@@ -1350,7 +1350,7 @@ func (s *Server) processOrgSample(ctx context.Context, req *OrgSampleRequest, to
13501350 } else {
13511351 // Fetch all PRs across the org modified since the date
13521352 var err error
1353- prs , err = github .FetchPRsFromOrg (ctx , req .Org , since , token )
1353+ prs , err = github .FetchPRsFromOrg (ctx , req .Org , since , token , nil )
13541354 if err != nil {
13551355 return nil , fmt .Errorf ("failed to fetch PRs: %w" , err )
13561356 }
@@ -1648,30 +1648,30 @@ func sendSSE(w http.ResponseWriter, update ProgressUpdate) error {
16481648// startKeepAlive starts a goroutine that sends SSE keep-alive comments every 2 seconds.
16491649// This prevents client-side timeouts during long operations.
16501650// Returns a stop channel (to stop keep-alive) and an error channel (signals connection failure).
1651- func startKeepAlive (w http.ResponseWriter ) (chan struct {}, <- chan error ) {
1652- stop := make (chan struct {})
1653- connErr := make (chan error , 1 )
1651+ func startKeepAlive (w http.ResponseWriter ) (stop chan struct {}, connErr <- chan error ) {
1652+ stopChan := make (chan struct {})
1653+ errChan := make (chan error , 1 )
16541654 go func () {
16551655 ticker := time .NewTicker (2 * time .Second )
16561656 defer ticker .Stop ()
1657- defer close (connErr )
1657+ defer close (errChan )
16581658 for {
16591659 select {
16601660 case <- ticker .C :
16611661 // Send SSE comment (keeps connection alive, ignored by client)
16621662 if _ , err := fmt .Fprint (w , ": keepalive\n \n " ); err != nil {
1663- connErr <- fmt .Errorf ("keepalive write failed: %w" , err )
1663+ errChan <- fmt .Errorf ("keepalive write failed: %w" , err )
16641664 return
16651665 }
16661666 if flusher , ok := w .(http.Flusher ); ok {
16671667 flusher .Flush ()
16681668 }
1669- case <- stop :
1669+ case <- stopChan :
16701670 return
16711671 }
16721672 }
16731673 }()
1674- return stop , connErr
1674+ return stopChan , errChan
16751675}
16761676
16771677// logSSEError logs an error from sendSSE if it occurs.
@@ -1737,10 +1737,19 @@ func (s *Server) processRepoSampleWithProgress(ctx context.Context, req *RepoSam
17371737 }
17381738 }()
17391739
1740- // Fetch all PRs modified since the date
1740+ // Fetch all PRs modified since the date with progress updates
17411741 var err error
1742+ progressCallback := func (queryName string , page int , prCount int ) {
1743+ logSSEError (ctx , s .logger , sendSSE (writer , ProgressUpdate {
1744+ Type : "fetching" ,
1745+ PR : 0 ,
1746+ Owner : req .Owner ,
1747+ Repo : req .Repo ,
1748+ Progress : fmt .Sprintf ("Fetching %s PRs (page %d, %d PRs found)..." , queryName , page , prCount ),
1749+ }))
1750+ }
17421751 //nolint:contextcheck // Using background context intentionally to prevent client timeout from canceling work
1743- prs , err = github .FetchPRsFromRepo (workCtx , req .Owner , req .Repo , since , token )
1752+ prs , err = github .FetchPRsFromRepo (workCtx , req .Owner , req .Repo , since , token , progressCallback )
17441753 if err != nil {
17451754 logSSEError (ctx , s .logger , sendSSE (writer , ProgressUpdate {
17461755 Type : "error" ,
@@ -1862,10 +1871,19 @@ func (s *Server) processOrgSampleWithProgress(ctx context.Context, req *OrgSampl
18621871 }
18631872 }()
18641873
1865- // Fetch all PRs across the org modified since the date
1874+ // Fetch all PRs across the org modified since the date with progress updates
18661875 var err error
1876+ progressCallback := func (queryName string , page int , prCount int ) {
1877+ logSSEError (ctx , s .logger , sendSSE (writer , ProgressUpdate {
1878+ Type : "fetching" ,
1879+ PR : 0 ,
1880+ Owner : req .Org ,
1881+ Repo : "" ,
1882+ Progress : fmt .Sprintf ("Fetching %s PRs (page %d, %d PRs found)..." , queryName , page , prCount ),
1883+ }))
1884+ }
18671885 //nolint:contextcheck // Using background context intentionally to prevent client timeout from canceling work
1868- prs , err = github .FetchPRsFromOrg (workCtx , req .Org , since , token )
1886+ prs , err = github .FetchPRsFromOrg (workCtx , req .Org , since , token , progressCallback )
18691887 if err != nil {
18701888 logSSEError (ctx , s .logger , sendSSE (writer , ProgressUpdate {
18711889 Type : "error" ,
0 commit comments