Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions github/billing.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,34 @@ type UsageReportOptions struct {
Hour *int `url:"hour,omitempty"`
}

// PremiumRequestUsageReportOptions specifies optional parameters
// for the enhanced billing platform premium request usage report.
type PremiumRequestUsageReportOptions struct {
// If specified, only return results for a single year.
// The value of year is an integer with four digits representing a year. For example, 2025.
// Default value is the current year.
Year *int `url:"year,omitempty"`

// If specified, only return results for a single month.
// The value of month is an integer between 1 and 12. Default value is the current month.
// If no year is specified the default year is used.
Month *int `url:"month,omitempty"`

// If specified, only return results for a single day.
// The value of day is an integer between 1 and 31.
// If no year or month is specified, the default year and month are used.
Day *int `url:"day,omitempty"`

// The user name to query usage for. The name is not case sensitive.
User *string `url:"user,omitempty"`

// The model name to query usage for. The name is not case sensitive.
Model *string `url:"model,omitempty"`

// The product name to query usage for. The name is not case sensitive.
Product *string `url:"product,omitempty"`
}

// UsageItem represents a single usage item in the enhanced billing platform report.
type UsageItem struct {
Date *string `json:"date"`
Expand All @@ -95,6 +123,38 @@ type UsageReport struct {
UsageItems []*UsageItem `json:"usageItems,omitempty"`
}

// PremiumRequestUsageItem represents a single usage line item in premium request usage reports.
type PremiumRequestUsageItem struct {
Product string `json:"product"`
SKU string `json:"sku"`
Model string `json:"model"`
UnitType string `json:"unitType"`
PricePerUnit float64 `json:"pricePerUnit"`
GrossQuantity int `json:"grossQuantity"`
GrossAmount float64 `json:"grossAmount"`
DiscountQuantity int `json:"discountQuantity"`
DiscountAmount float64 `json:"discountAmount"`
NetQuantity int `json:"netQuantity"`
NetAmount float64 `json:"netAmount"`
}

// PremiumRequestUsageTimePeriod represents a time period for premium request usage reports.
type PremiumRequestUsageTimePeriod struct {
Year int `json:"year"`
Month *int `json:"month,omitempty"`
Day *int `json:"day,omitempty"`
}

// PremiumRequestUsageReport represents the premium request usage report response.
type PremiumRequestUsageReport struct {
TimePeriod PremiumRequestUsageTimePeriod `json:"timePeriod"`
Organization string `json:"organization"`
User *string `json:"user,omitempty"`
Product *string `json:"product,omitempty"`
Model *string `json:"model,omitempty"`
UsageItems []*PremiumRequestUsageItem `json:"usageItems"`
}

// GetPackagesBillingOrg returns the free and paid storage used for GitHub Packages in gigabytes for an Org.
//
// GitHub API docs: https://docs.github.com/rest/billing/billing#get-github-packages-billing-for-an-organization
Expand Down Expand Up @@ -262,3 +322,61 @@ func (s *BillingService) GetUsageReportUser(ctx context.Context, user string, op

return usageReport, resp, nil
}

// GetOrganizationPremiumRequestUsageReport returns a report of the premium request
// usage for an organization using the enhanced billing platform.
//
// Note: This endpoint is only available to organizations with access to the enhanced billing platform.
//
// GitHub API docs: https://docs.github.com/rest/billing/enhanced-billing#get-billing-premium-request-usage-report-for-an-organization
//
//meta:operation GET /organizations/{org}/settings/billing/premium_request/usage
func (s *BillingService) GetOrganizationPremiumRequestUsageReport(ctx context.Context, org string, opts *PremiumRequestUsageReportOptions) (*PremiumRequestUsageReport, *Response, error) {
u := fmt.Sprintf("organizations/%v/settings/billing/premium_request/usage", org)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}

req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}

premiumRequestUsageReport := new(PremiumRequestUsageReport)
resp, err := s.client.Do(ctx, req, premiumRequestUsageReport)
if err != nil {
return nil, resp, err
}

return premiumRequestUsageReport, resp, nil
}

// GetPremiumRequestUsageReport returns a report of the premium request
// usage for a user using the enhanced billing platform.
//
// Note: This endpoint is only available to users with access to the enhanced billing platform.
//
// GitHub API docs: https://docs.github.com/rest/billing/enhanced-billing#get-billing-premium-request-usage-report-for-a-user
//
//meta:operation GET /users/{username}/settings/billing/premium_request/usage
func (s *BillingService) GetPremiumRequestUsageReport(ctx context.Context, user string, opts *PremiumRequestUsageReportOptions) (*PremiumRequestUsageReport, *Response, error) {
u := fmt.Sprintf("users/%v/settings/billing/premium_request/usage", user)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}

req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}

premiumRequestUsageReport := new(PremiumRequestUsageReport)
resp, err := s.client.Do(ctx, req, premiumRequestUsageReport)
if err != nil {
return nil, resp, err
}

return premiumRequestUsageReport, resp, nil
}
192 changes: 192 additions & 0 deletions github/billing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -520,3 +520,195 @@ func TestBillingService_GetUsageReportUser_invalidUser(t *testing.T) {
_, _, err := client.Billing.GetUsageReportUser(ctx, "%", nil)
testURLParseError(t, err)
}

func TestBillingService_GetOrganizationPremiumRequestUsageReport(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)
mux.HandleFunc("/organizations/o/settings/billing/premium_request/usage", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testFormValues(t, r, values{
"year": "2025",
"month": "10",
"user": "testuser",
})
fmt.Fprint(w, `{
"timePeriod": {
"year": 2025,
"month": 10
},
"organization": "GitHub",
"user": "testuser",
"product": "Copilot",
"model": "GPT-5",
"usageItems": [
{
"product": "Copilot",
"sku": "Copilot Premium Request",
"model": "GPT-5",
"unitType": "requests",
"pricePerUnit": 0.04,
"grossQuantity": 100,
"grossAmount": 4.0,
"discountQuantity": 0,
"discountAmount": 0.0,
"netQuantity": 100,
"netAmount": 4.0
}
]
}`)
})
ctx := t.Context()
opts := &PremiumRequestUsageReportOptions{
Year: Ptr(2025),
Month: Ptr(10),
User: Ptr("testuser"),
}
report, _, err := client.Billing.GetOrganizationPremiumRequestUsageReport(ctx, "o", opts)
if err != nil {
t.Errorf("Billing.GetOrganizationPremiumRequestUsageReport returned error: %v", err)
}
want := &PremiumRequestUsageReport{
TimePeriod: PremiumRequestUsageTimePeriod{
Year: 2025,
Month: Ptr(10),
},
Organization: "GitHub",
User: Ptr("testuser"),
Product: Ptr("Copilot"),
Model: Ptr("GPT-5"),
UsageItems: []*PremiumRequestUsageItem{
{
Product: "Copilot",
SKU: "Copilot Premium Request",
Model: "GPT-5",
UnitType: "requests",
PricePerUnit: 0.04,
GrossQuantity: 100,
GrossAmount: 4.0,
DiscountQuantity: 0,
DiscountAmount: 0.0,
NetQuantity: 100,
NetAmount: 4.0,
},
},
}
if !cmp.Equal(report, want) {
t.Errorf("Billing.GetOrganizationPremiumRequestUsageReport returned %+v, want %+v", report, want)
}

const methodName = "GetOrganizationPremiumRequestUsageReport"
testBadOptions(t, methodName, func() (err error) {
_, _, err = client.Billing.GetOrganizationPremiumRequestUsageReport(ctx, "\n", opts)
return err
})

testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
got, resp, err := client.Billing.GetOrganizationPremiumRequestUsageReport(ctx, "o", nil)
if got != nil {
t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
}
return resp, err
})
}

func TestBillingService_GetOrganizationPremiumRequestUsageReport_invalidOrg(t *testing.T) {
t.Parallel()
client, _, _ := setup(t)

ctx := t.Context()
_, _, err := client.Billing.GetOrganizationPremiumRequestUsageReport(ctx, "%", nil)
testURLParseError(t, err)
}

func TestBillingService_GetPremiumRequestUsageReport(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)
mux.HandleFunc("/users/u/settings/billing/premium_request/usage", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testFormValues(t, r, values{
"year": "2025",
"day": "15",
})
fmt.Fprint(w, `{
"timePeriod": {
"year": 2025,
"day": 15
},
"organization": "UserOrg",
"product": "Copilot",
"usageItems": [
{
"product": "Copilot",
"sku": "Copilot Premium Request",
"model": "GPT-4",
"unitType": "requests",
"pricePerUnit": 0.02,
"grossQuantity": 50,
"grossAmount": 1.0,
"discountQuantity": 5,
"discountAmount": 0.1,
"netQuantity": 45,
"netAmount": 0.9
}
]
}`)
})
ctx := t.Context()
opts := &PremiumRequestUsageReportOptions{
Year: Ptr(2025),
Day: Ptr(15),
}
report, _, err := client.Billing.GetPremiumRequestUsageReport(ctx, "u", opts)
if err != nil {
t.Errorf("Billing.GetPremiumRequestUsageReport returned error: %v", err)
}
want := &PremiumRequestUsageReport{
TimePeriod: PremiumRequestUsageTimePeriod{
Year: 2025,
Day: Ptr(15),
},
Organization: "UserOrg",
Product: Ptr("Copilot"),
UsageItems: []*PremiumRequestUsageItem{
{
Product: "Copilot",
SKU: "Copilot Premium Request",
Model: "GPT-4",
UnitType: "requests",
PricePerUnit: 0.02,
GrossQuantity: 50,
GrossAmount: 1.0,
DiscountQuantity: 5,
DiscountAmount: 0.1,
NetQuantity: 45,
NetAmount: 0.9,
},
},
}
if !cmp.Equal(report, want) {
t.Errorf("Billing.GetPremiumRequestUsageReport returned %+v, want %+v", report, want)
}

const methodName = "GetPremiumRequestUsageReport"
testBadOptions(t, methodName, func() (err error) {
_, _, err = client.Billing.GetPremiumRequestUsageReport(ctx, "\n", opts)
return err
})

testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
got, resp, err := client.Billing.GetPremiumRequestUsageReport(ctx, "u", nil)
if got != nil {
t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
}
return resp, err
})
}

func TestBillingService_GetPremiumRequestUsageReport_invalidUser(t *testing.T) {
t.Parallel()
client, _, _ := setup(t)

ctx := t.Context()
_, _, err := client.Billing.GetPremiumRequestUsageReport(ctx, "%", nil)
testURLParseError(t, err)
}
Loading
Loading