Files
kandev/apps/backend/internal/github/rate_limit_fetch.go
T
2026-08-14 17:04:04 +01:00

72 lines
1.8 KiB
Go

package github
import (
"context"
"encoding/json"
"fmt"
"time"
)
// rateLimitResponse mirrors the GitHub GET /rate_limit JSON shape. The
// endpoint includes additional resource buckets, but these are the buckets
// Kandev tracks and displays.
type rateLimitResponse struct {
Resources rateLimitResources `json:"resources"`
}
type rateLimitResources struct {
Core rateLimitBucket `json:"core"`
GraphQL rateLimitBucket `json:"graphql"`
Search rateLimitBucket `json:"search"`
}
type rateLimitBucket struct {
Limit int `json:"limit"`
Remaining int `json:"remaining"`
Reset int64 `json:"reset"`
}
func recordRateLimitResources(tracker *RateTracker, resources rateLimitResources) {
if tracker == nil {
return
}
now := time.Now().UTC()
record := func(resource Resource, bucket rateLimitBucket) {
if bucket.Limit == 0 && bucket.Remaining == 0 && bucket.Reset == 0 {
return
}
tracker.Record(RateSnapshot{
Resource: resource,
Limit: bucket.Limit,
Remaining: bucket.Remaining,
ResetAt: time.Unix(bucket.Reset, 0).UTC(),
UpdatedAt: now,
})
}
record(ResourceCore, resources.Core)
record(ResourceGraphQL, resources.GraphQL)
record(ResourceSearch, resources.Search)
}
// FetchRateLimit calls GET /rate_limit and seeds the client's tracker with
// the current REST, GraphQL, and search buckets.
func (c *TokenClient) FetchRateLimit(ctx context.Context) error {
if c.rateTracker == nil {
return nil
}
var raw rateLimitResponse
if err := c.get(ctx, "/rate_limit", &raw); err != nil {
return fmt.Errorf("fetch rate limit: %w", err)
}
recordRateLimitResources(c.rateTracker, raw.Resources)
return nil
}
func decodeRateLimitResponse(data []byte) (rateLimitResponse, error) {
var raw rateLimitResponse
if err := json.Unmarshal(data, &raw); err != nil {
return rateLimitResponse{}, err
}
return raw, nil
}