diff --git a/github/billing.go b/github/billing.go index b98af40a398..ae3d3b50475 100644 --- a/github/billing.go +++ b/github/billing.go @@ -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"` @@ -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 @@ -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 +} diff --git a/github/billing_test.go b/github/billing_test.go index d9c6fa725b2..d7bc6fa34e7 100644 --- a/github/billing_test.go +++ b/github/billing_test.go @@ -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) +} diff --git a/github/github-accessors.go b/github/github-accessors.go index 0a8f83ee2e1..a6e79e270f5 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -18422,6 +18422,94 @@ func (p *Plan) GetSpace() int { return *p.Space } +// GetModel returns the Model field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageReport) GetModel() string { + if p == nil || p.Model == nil { + return "" + } + return *p.Model +} + +// GetProduct returns the Product field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageReport) GetProduct() string { + if p == nil || p.Product == nil { + return "" + } + return *p.Product +} + +// GetUser returns the User field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageReport) GetUser() string { + if p == nil || p.User == nil { + return "" + } + return *p.User +} + +// GetDay returns the Day field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageReportOptions) GetDay() int { + if p == nil || p.Day == nil { + return 0 + } + return *p.Day +} + +// GetModel returns the Model field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageReportOptions) GetModel() string { + if p == nil || p.Model == nil { + return "" + } + return *p.Model +} + +// GetMonth returns the Month field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageReportOptions) GetMonth() int { + if p == nil || p.Month == nil { + return 0 + } + return *p.Month +} + +// GetProduct returns the Product field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageReportOptions) GetProduct() string { + if p == nil || p.Product == nil { + return "" + } + return *p.Product +} + +// GetUser returns the User field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageReportOptions) GetUser() string { + if p == nil || p.User == nil { + return "" + } + return *p.User +} + +// GetYear returns the Year field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageReportOptions) GetYear() int { + if p == nil || p.Year == nil { + return 0 + } + return *p.Year +} + +// GetDay returns the Day field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageTimePeriod) GetDay() int { + if p == nil || p.Day == nil { + return 0 + } + return *p.Day +} + +// GetMonth returns the Month field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageTimePeriod) GetMonth() int { + if p == nil || p.Month == nil { + return 0 + } + return *p.Month +} + // GetConfigURL returns the ConfigURL field if it's non-nil, zero value otherwise. func (p *PreReceiveHook) GetConfigURL() string { if p == nil || p.ConfigURL == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 3c78a1436b8..73a35b4641d 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -23934,6 +23934,127 @@ func TestPlan_GetSpace(tt *testing.T) { p.GetSpace() } +func TestPremiumRequestUsageReport_GetModel(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PremiumRequestUsageReport{Model: &zeroValue} + p.GetModel() + p = &PremiumRequestUsageReport{} + p.GetModel() + p = nil + p.GetModel() +} + +func TestPremiumRequestUsageReport_GetProduct(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PremiumRequestUsageReport{Product: &zeroValue} + p.GetProduct() + p = &PremiumRequestUsageReport{} + p.GetProduct() + p = nil + p.GetProduct() +} + +func TestPremiumRequestUsageReport_GetUser(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PremiumRequestUsageReport{User: &zeroValue} + p.GetUser() + p = &PremiumRequestUsageReport{} + p.GetUser() + p = nil + p.GetUser() +} + +func TestPremiumRequestUsageReportOptions_GetDay(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PremiumRequestUsageReportOptions{Day: &zeroValue} + p.GetDay() + p = &PremiumRequestUsageReportOptions{} + p.GetDay() + p = nil + p.GetDay() +} + +func TestPremiumRequestUsageReportOptions_GetModel(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PremiumRequestUsageReportOptions{Model: &zeroValue} + p.GetModel() + p = &PremiumRequestUsageReportOptions{} + p.GetModel() + p = nil + p.GetModel() +} + +func TestPremiumRequestUsageReportOptions_GetMonth(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PremiumRequestUsageReportOptions{Month: &zeroValue} + p.GetMonth() + p = &PremiumRequestUsageReportOptions{} + p.GetMonth() + p = nil + p.GetMonth() +} + +func TestPremiumRequestUsageReportOptions_GetProduct(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PremiumRequestUsageReportOptions{Product: &zeroValue} + p.GetProduct() + p = &PremiumRequestUsageReportOptions{} + p.GetProduct() + p = nil + p.GetProduct() +} + +func TestPremiumRequestUsageReportOptions_GetUser(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PremiumRequestUsageReportOptions{User: &zeroValue} + p.GetUser() + p = &PremiumRequestUsageReportOptions{} + p.GetUser() + p = nil + p.GetUser() +} + +func TestPremiumRequestUsageReportOptions_GetYear(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PremiumRequestUsageReportOptions{Year: &zeroValue} + p.GetYear() + p = &PremiumRequestUsageReportOptions{} + p.GetYear() + p = nil + p.GetYear() +} + +func TestPremiumRequestUsageTimePeriod_GetDay(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PremiumRequestUsageTimePeriod{Day: &zeroValue} + p.GetDay() + p = &PremiumRequestUsageTimePeriod{} + p.GetDay() + p = nil + p.GetDay() +} + +func TestPremiumRequestUsageTimePeriod_GetMonth(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PremiumRequestUsageTimePeriod{Month: &zeroValue} + p.GetMonth() + p = &PremiumRequestUsageTimePeriod{} + p.GetMonth() + p = nil + p.GetMonth() +} + func TestPreReceiveHook_GetConfigURL(tt *testing.T) { tt.Parallel() var zeroValue string