Skip to content

Commit 29b4fac

Browse files
authored
feat: Add premium request usage report endpoints for organizations and users (#3751)
1 parent 52f9fff commit 29b4fac

File tree

4 files changed

+519
-0
lines changed

4 files changed

+519
-0
lines changed

github/billing.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,34 @@ type UsageReportOptions struct {
7474
Hour *int `url:"hour,omitempty"`
7575
}
7676

77+
// PremiumRequestUsageReportOptions specifies optional parameters
78+
// for the enhanced billing platform premium request usage report.
79+
type PremiumRequestUsageReportOptions struct {
80+
// If specified, only return results for a single year.
81+
// The value of year is an integer with four digits representing a year. For example, 2025.
82+
// Default value is the current year.
83+
Year *int `url:"year,omitempty"`
84+
85+
// If specified, only return results for a single month.
86+
// The value of month is an integer between 1 and 12. Default value is the current month.
87+
// If no year is specified the default year is used.
88+
Month *int `url:"month,omitempty"`
89+
90+
// If specified, only return results for a single day.
91+
// The value of day is an integer between 1 and 31.
92+
// If no year or month is specified, the default year and month are used.
93+
Day *int `url:"day,omitempty"`
94+
95+
// The user name to query usage for. The name is not case sensitive.
96+
User *string `url:"user,omitempty"`
97+
98+
// The model name to query usage for. The name is not case sensitive.
99+
Model *string `url:"model,omitempty"`
100+
101+
// The product name to query usage for. The name is not case sensitive.
102+
Product *string `url:"product,omitempty"`
103+
}
104+
77105
// UsageItem represents a single usage item in the enhanced billing platform report.
78106
type UsageItem struct {
79107
Date *string `json:"date"`
@@ -95,6 +123,38 @@ type UsageReport struct {
95123
UsageItems []*UsageItem `json:"usageItems,omitempty"`
96124
}
97125

126+
// PremiumRequestUsageItem represents a single usage line item in premium request usage reports.
127+
type PremiumRequestUsageItem struct {
128+
Product string `json:"product"`
129+
SKU string `json:"sku"`
130+
Model string `json:"model"`
131+
UnitType string `json:"unitType"`
132+
PricePerUnit float64 `json:"pricePerUnit"`
133+
GrossQuantity int `json:"grossQuantity"`
134+
GrossAmount float64 `json:"grossAmount"`
135+
DiscountQuantity int `json:"discountQuantity"`
136+
DiscountAmount float64 `json:"discountAmount"`
137+
NetQuantity int `json:"netQuantity"`
138+
NetAmount float64 `json:"netAmount"`
139+
}
140+
141+
// PremiumRequestUsageTimePeriod represents a time period for premium request usage reports.
142+
type PremiumRequestUsageTimePeriod struct {
143+
Year int `json:"year"`
144+
Month *int `json:"month,omitempty"`
145+
Day *int `json:"day,omitempty"`
146+
}
147+
148+
// PremiumRequestUsageReport represents the premium request usage report response.
149+
type PremiumRequestUsageReport struct {
150+
TimePeriod PremiumRequestUsageTimePeriod `json:"timePeriod"`
151+
Organization string `json:"organization"`
152+
User *string `json:"user,omitempty"`
153+
Product *string `json:"product,omitempty"`
154+
Model *string `json:"model,omitempty"`
155+
UsageItems []*PremiumRequestUsageItem `json:"usageItems"`
156+
}
157+
98158
// GetPackagesBillingOrg returns the free and paid storage used for GitHub Packages in gigabytes for an Org.
99159
//
100160
// GitHub API docs: https://docs.github.com/rest/billing/billing#get-github-packages-billing-for-an-organization
@@ -262,3 +322,61 @@ func (s *BillingService) GetUsageReportUser(ctx context.Context, user string, op
262322

263323
return usageReport, resp, nil
264324
}
325+
326+
// GetOrganizationPremiumRequestUsageReport returns a report of the premium request
327+
// usage for an organization using the enhanced billing platform.
328+
//
329+
// Note: This endpoint is only available to organizations with access to the enhanced billing platform.
330+
//
331+
// GitHub API docs: https://docs.github.com/rest/billing/enhanced-billing#get-billing-premium-request-usage-report-for-an-organization
332+
//
333+
//meta:operation GET /organizations/{org}/settings/billing/premium_request/usage
334+
func (s *BillingService) GetOrganizationPremiumRequestUsageReport(ctx context.Context, org string, opts *PremiumRequestUsageReportOptions) (*PremiumRequestUsageReport, *Response, error) {
335+
u := fmt.Sprintf("organizations/%v/settings/billing/premium_request/usage", org)
336+
u, err := addOptions(u, opts)
337+
if err != nil {
338+
return nil, nil, err
339+
}
340+
341+
req, err := s.client.NewRequest("GET", u, nil)
342+
if err != nil {
343+
return nil, nil, err
344+
}
345+
346+
premiumRequestUsageReport := new(PremiumRequestUsageReport)
347+
resp, err := s.client.Do(ctx, req, premiumRequestUsageReport)
348+
if err != nil {
349+
return nil, resp, err
350+
}
351+
352+
return premiumRequestUsageReport, resp, nil
353+
}
354+
355+
// GetPremiumRequestUsageReport returns a report of the premium request
356+
// usage for a user using the enhanced billing platform.
357+
//
358+
// Note: This endpoint is only available to users with access to the enhanced billing platform.
359+
//
360+
// GitHub API docs: https://docs.github.com/rest/billing/enhanced-billing#get-billing-premium-request-usage-report-for-a-user
361+
//
362+
//meta:operation GET /users/{username}/settings/billing/premium_request/usage
363+
func (s *BillingService) GetPremiumRequestUsageReport(ctx context.Context, user string, opts *PremiumRequestUsageReportOptions) (*PremiumRequestUsageReport, *Response, error) {
364+
u := fmt.Sprintf("users/%v/settings/billing/premium_request/usage", user)
365+
u, err := addOptions(u, opts)
366+
if err != nil {
367+
return nil, nil, err
368+
}
369+
370+
req, err := s.client.NewRequest("GET", u, nil)
371+
if err != nil {
372+
return nil, nil, err
373+
}
374+
375+
premiumRequestUsageReport := new(PremiumRequestUsageReport)
376+
resp, err := s.client.Do(ctx, req, premiumRequestUsageReport)
377+
if err != nil {
378+
return nil, resp, err
379+
}
380+
381+
return premiumRequestUsageReport, resp, nil
382+
}

github/billing_test.go

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,3 +520,195 @@ func TestBillingService_GetUsageReportUser_invalidUser(t *testing.T) {
520520
_, _, err := client.Billing.GetUsageReportUser(ctx, "%", nil)
521521
testURLParseError(t, err)
522522
}
523+
524+
func TestBillingService_GetOrganizationPremiumRequestUsageReport(t *testing.T) {
525+
t.Parallel()
526+
client, mux, _ := setup(t)
527+
mux.HandleFunc("/organizations/o/settings/billing/premium_request/usage", func(w http.ResponseWriter, r *http.Request) {
528+
testMethod(t, r, "GET")
529+
testFormValues(t, r, values{
530+
"year": "2025",
531+
"month": "10",
532+
"user": "testuser",
533+
})
534+
fmt.Fprint(w, `{
535+
"timePeriod": {
536+
"year": 2025,
537+
"month": 10
538+
},
539+
"organization": "GitHub",
540+
"user": "testuser",
541+
"product": "Copilot",
542+
"model": "GPT-5",
543+
"usageItems": [
544+
{
545+
"product": "Copilot",
546+
"sku": "Copilot Premium Request",
547+
"model": "GPT-5",
548+
"unitType": "requests",
549+
"pricePerUnit": 0.04,
550+
"grossQuantity": 100,
551+
"grossAmount": 4.0,
552+
"discountQuantity": 0,
553+
"discountAmount": 0.0,
554+
"netQuantity": 100,
555+
"netAmount": 4.0
556+
}
557+
]
558+
}`)
559+
})
560+
ctx := t.Context()
561+
opts := &PremiumRequestUsageReportOptions{
562+
Year: Ptr(2025),
563+
Month: Ptr(10),
564+
User: Ptr("testuser"),
565+
}
566+
report, _, err := client.Billing.GetOrganizationPremiumRequestUsageReport(ctx, "o", opts)
567+
if err != nil {
568+
t.Errorf("Billing.GetOrganizationPremiumRequestUsageReport returned error: %v", err)
569+
}
570+
want := &PremiumRequestUsageReport{
571+
TimePeriod: PremiumRequestUsageTimePeriod{
572+
Year: 2025,
573+
Month: Ptr(10),
574+
},
575+
Organization: "GitHub",
576+
User: Ptr("testuser"),
577+
Product: Ptr("Copilot"),
578+
Model: Ptr("GPT-5"),
579+
UsageItems: []*PremiumRequestUsageItem{
580+
{
581+
Product: "Copilot",
582+
SKU: "Copilot Premium Request",
583+
Model: "GPT-5",
584+
UnitType: "requests",
585+
PricePerUnit: 0.04,
586+
GrossQuantity: 100,
587+
GrossAmount: 4.0,
588+
DiscountQuantity: 0,
589+
DiscountAmount: 0.0,
590+
NetQuantity: 100,
591+
NetAmount: 4.0,
592+
},
593+
},
594+
}
595+
if !cmp.Equal(report, want) {
596+
t.Errorf("Billing.GetOrganizationPremiumRequestUsageReport returned %+v, want %+v", report, want)
597+
}
598+
599+
const methodName = "GetOrganizationPremiumRequestUsageReport"
600+
testBadOptions(t, methodName, func() (err error) {
601+
_, _, err = client.Billing.GetOrganizationPremiumRequestUsageReport(ctx, "\n", opts)
602+
return err
603+
})
604+
605+
testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
606+
got, resp, err := client.Billing.GetOrganizationPremiumRequestUsageReport(ctx, "o", nil)
607+
if got != nil {
608+
t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
609+
}
610+
return resp, err
611+
})
612+
}
613+
614+
func TestBillingService_GetOrganizationPremiumRequestUsageReport_invalidOrg(t *testing.T) {
615+
t.Parallel()
616+
client, _, _ := setup(t)
617+
618+
ctx := t.Context()
619+
_, _, err := client.Billing.GetOrganizationPremiumRequestUsageReport(ctx, "%", nil)
620+
testURLParseError(t, err)
621+
}
622+
623+
func TestBillingService_GetPremiumRequestUsageReport(t *testing.T) {
624+
t.Parallel()
625+
client, mux, _ := setup(t)
626+
mux.HandleFunc("/users/u/settings/billing/premium_request/usage", func(w http.ResponseWriter, r *http.Request) {
627+
testMethod(t, r, "GET")
628+
testFormValues(t, r, values{
629+
"year": "2025",
630+
"day": "15",
631+
})
632+
fmt.Fprint(w, `{
633+
"timePeriod": {
634+
"year": 2025,
635+
"day": 15
636+
},
637+
"organization": "UserOrg",
638+
"product": "Copilot",
639+
"usageItems": [
640+
{
641+
"product": "Copilot",
642+
"sku": "Copilot Premium Request",
643+
"model": "GPT-4",
644+
"unitType": "requests",
645+
"pricePerUnit": 0.02,
646+
"grossQuantity": 50,
647+
"grossAmount": 1.0,
648+
"discountQuantity": 5,
649+
"discountAmount": 0.1,
650+
"netQuantity": 45,
651+
"netAmount": 0.9
652+
}
653+
]
654+
}`)
655+
})
656+
ctx := t.Context()
657+
opts := &PremiumRequestUsageReportOptions{
658+
Year: Ptr(2025),
659+
Day: Ptr(15),
660+
}
661+
report, _, err := client.Billing.GetPremiumRequestUsageReport(ctx, "u", opts)
662+
if err != nil {
663+
t.Errorf("Billing.GetPremiumRequestUsageReport returned error: %v", err)
664+
}
665+
want := &PremiumRequestUsageReport{
666+
TimePeriod: PremiumRequestUsageTimePeriod{
667+
Year: 2025,
668+
Day: Ptr(15),
669+
},
670+
Organization: "UserOrg",
671+
Product: Ptr("Copilot"),
672+
UsageItems: []*PremiumRequestUsageItem{
673+
{
674+
Product: "Copilot",
675+
SKU: "Copilot Premium Request",
676+
Model: "GPT-4",
677+
UnitType: "requests",
678+
PricePerUnit: 0.02,
679+
GrossQuantity: 50,
680+
GrossAmount: 1.0,
681+
DiscountQuantity: 5,
682+
DiscountAmount: 0.1,
683+
NetQuantity: 45,
684+
NetAmount: 0.9,
685+
},
686+
},
687+
}
688+
if !cmp.Equal(report, want) {
689+
t.Errorf("Billing.GetPremiumRequestUsageReport returned %+v, want %+v", report, want)
690+
}
691+
692+
const methodName = "GetPremiumRequestUsageReport"
693+
testBadOptions(t, methodName, func() (err error) {
694+
_, _, err = client.Billing.GetPremiumRequestUsageReport(ctx, "\n", opts)
695+
return err
696+
})
697+
698+
testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
699+
got, resp, err := client.Billing.GetPremiumRequestUsageReport(ctx, "u", nil)
700+
if got != nil {
701+
t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
702+
}
703+
return resp, err
704+
})
705+
}
706+
707+
func TestBillingService_GetPremiumRequestUsageReport_invalidUser(t *testing.T) {
708+
t.Parallel()
709+
client, _, _ := setup(t)
710+
711+
ctx := t.Context()
712+
_, _, err := client.Billing.GetPremiumRequestUsageReport(ctx, "%", nil)
713+
testURLParseError(t, err)
714+
}

0 commit comments

Comments
 (0)