Skip to content

Commit 9c9cddf

Browse files
committed
1 parent f1b11e1 commit 9c9cddf

File tree

4 files changed

+528
-0
lines changed

4 files changed

+528
-0
lines changed

github/billing.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,41 @@ type AdvancedSecurityCommittersBreakdown struct {
6363
LastPushedDate *string `json:"last_pushed_date,omitempty"`
6464
}
6565

66+
// UsageReportOptions specifies optional parameters for the enhanced billing platform usage report.
67+
type UsageReportOptions struct {
68+
// If specified, only return results for a single year. The value of year is an integer with four digits representing a year.
69+
Year *int `url:"year,omitempty"`
70+
71+
// If specified, only return results for a single month. The value of month is an integer between 1 and 12.
72+
Month *int `url:"month,omitempty"`
73+
74+
// If specified, only return results for a single day. The value of day is an integer between 1 and 31.
75+
Day *int `url:"day,omitempty"`
76+
77+
// If specified, only return results for a single hour. The value of hour is an integer between 0 and 23.
78+
Hour *int `url:"hour,omitempty"`
79+
}
80+
81+
// UsageItem represents a single usage item in the enhanced billing platform report.
82+
type UsageItem struct {
83+
Date *string `json:"date,omitempty"`
84+
Product *string `json:"product,omitempty"`
85+
SKU *string `json:"sku,omitempty"`
86+
Quantity *int `json:"quantity,omitempty"`
87+
UnitType *string `json:"unitType,omitempty"`
88+
PricePerUnit *float64 `json:"pricePerUnit,omitempty"`
89+
GrossAmount *float64 `json:"grossAmount,omitempty"`
90+
DiscountAmount *float64 `json:"discountAmount,omitempty"`
91+
NetAmount *float64 `json:"netAmount,omitempty"`
92+
OrganizationName *string `json:"organizationName,omitempty"`
93+
RepositoryName *string `json:"repositoryName,omitempty"`
94+
}
95+
96+
// UsageReport represents the enhanced billing platform usage report response.
97+
type UsageReport struct {
98+
UsageItems []*UsageItem `json:"usageItems,omitempty"`
99+
}
100+
66101
// GetActionsBillingOrg returns the summary of the free and paid GitHub Actions minutes used for an Org.
67102
//
68103
// GitHub API docs: https://docs.github.com/rest/billing/billing#get-github-actions-billing-for-an-organization
@@ -216,3 +251,59 @@ func (s *BillingService) GetStorageBillingUser(ctx context.Context, user string)
216251

217252
return storageUserBilling, resp, nil
218253
}
254+
255+
// GetUsageReportOrg returns a report of the total usage for an organization using the enhanced billing platform.
256+
//
257+
// Note: This endpoint is only available to organizations with access to the enhanced billing platform.
258+
//
259+
// GitHub API docs: https://docs.github.com/rest/billing/enhanced-billing#get-billing-usage-report-for-an-organization
260+
//
261+
//meta:operation GET /organizations/{org}/settings/billing/usage
262+
func (s *BillingService) GetUsageReportOrg(ctx context.Context, org string, opts *UsageReportOptions) (*UsageReport, *Response, error) {
263+
u := fmt.Sprintf("organizations/%v/settings/billing/usage", org)
264+
u, err := addOptions(u, opts)
265+
if err != nil {
266+
return nil, nil, err
267+
}
268+
269+
req, err := s.client.NewRequest("GET", u, nil)
270+
if err != nil {
271+
return nil, nil, err
272+
}
273+
274+
usageReport := new(UsageReport)
275+
resp, err := s.client.Do(ctx, req, usageReport)
276+
if err != nil {
277+
return nil, resp, err
278+
}
279+
280+
return usageReport, resp, nil
281+
}
282+
283+
// GetUsageReportUser returns a report of the total usage for a user using the enhanced billing platform.
284+
//
285+
// Note: This endpoint is only available to users with access to the enhanced billing platform.
286+
//
287+
// GitHub API docs: https://docs.github.com/rest/billing/enhanced-billing#get-billing-usage-report-for-a-user
288+
//
289+
//meta:operation GET /users/{username}/settings/billing/usage
290+
func (s *BillingService) GetUsageReportUser(ctx context.Context, user string, opts *UsageReportOptions) (*UsageReport, *Response, error) {
291+
u := fmt.Sprintf("users/%v/settings/billing/usage", user)
292+
u, err := addOptions(u, opts)
293+
if err != nil {
294+
return nil, nil, err
295+
}
296+
297+
req, err := s.client.NewRequest("GET", u, nil)
298+
if err != nil {
299+
return nil, nil, err
300+
}
301+
302+
usageReport := new(UsageReport)
303+
resp, err := s.client.Do(ctx, req, usageReport)
304+
if err != nil {
305+
return nil, resp, err
306+
}
307+
308+
return usageReport, resp, nil
309+
}

github/billing_test.go

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,3 +510,167 @@ func TestBillingService_GetAdvancedSecurityActiveCommittersOrg_invalidOrg(t *tes
510510
_, _, err := client.Billing.GetAdvancedSecurityActiveCommittersOrg(ctx, "%", nil)
511511
testURLParseError(t, err)
512512
}
513+
514+
func TestBillingService_GetUsageReportOrg(t *testing.T) {
515+
t.Parallel()
516+
client, mux, _ := setup(t)
517+
518+
mux.HandleFunc("/organizations/o/settings/billing/usage", func(w http.ResponseWriter, r *http.Request) {
519+
testMethod(t, r, "GET")
520+
testFormValues(t, r, values{
521+
"year": "2023",
522+
"month": "8",
523+
})
524+
fmt.Fprint(w, `{
525+
"usageItems": [
526+
{
527+
"date": "2023-08-01",
528+
"product": "Actions",
529+
"sku": "Actions Linux",
530+
"quantity": 100,
531+
"unitType": "minutes",
532+
"pricePerUnit": 0.008,
533+
"grossAmount": 0.8,
534+
"discountAmount": 0,
535+
"netAmount": 0.8,
536+
"organizationName": "GitHub",
537+
"repositoryName": "github/example"
538+
}
539+
]
540+
}`)
541+
})
542+
543+
ctx := context.Background()
544+
opts := &UsageReportOptions{
545+
Year: Ptr(2023),
546+
Month: Ptr(8),
547+
}
548+
report, _, err := client.Billing.GetUsageReportOrg(ctx, "o", opts)
549+
if err != nil {
550+
t.Errorf("Billing.GetUsageReportOrg returned error: %v", err)
551+
}
552+
553+
want := &UsageReport{
554+
UsageItems: []*UsageItem{
555+
{
556+
Date: Ptr("2023-08-01"),
557+
Product: Ptr("Actions"),
558+
SKU: Ptr("Actions Linux"),
559+
Quantity: Ptr(100),
560+
UnitType: Ptr("minutes"),
561+
PricePerUnit: Ptr(0.008),
562+
GrossAmount: Ptr(0.8),
563+
DiscountAmount: Ptr(0.0),
564+
NetAmount: Ptr(0.8),
565+
OrganizationName: Ptr("GitHub"),
566+
RepositoryName: Ptr("github/example"),
567+
},
568+
},
569+
}
570+
if !cmp.Equal(report, want) {
571+
t.Errorf("Billing.GetUsageReportOrg returned %+v, want %+v", report, want)
572+
}
573+
574+
const methodName = "GetUsageReportOrg"
575+
testBadOptions(t, methodName, func() (err error) {
576+
_, _, err = client.Billing.GetUsageReportOrg(ctx, "\n", nil)
577+
return err
578+
})
579+
580+
testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
581+
got, resp, err := client.Billing.GetUsageReportOrg(ctx, "o", nil)
582+
if got != nil {
583+
t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
584+
}
585+
return resp, err
586+
})
587+
}
588+
589+
func TestBillingService_GetUsageReportOrg_invalidOrg(t *testing.T) {
590+
t.Parallel()
591+
client, _, _ := setup(t)
592+
593+
ctx := context.Background()
594+
_, _, err := client.Billing.GetUsageReportOrg(ctx, "%", nil)
595+
testURLParseError(t, err)
596+
}
597+
598+
func TestBillingService_GetUsageReportUser(t *testing.T) {
599+
t.Parallel()
600+
client, mux, _ := setup(t)
601+
602+
mux.HandleFunc("/users/u/settings/billing/usage", func(w http.ResponseWriter, r *http.Request) {
603+
testMethod(t, r, "GET")
604+
testFormValues(t, r, values{
605+
"day": "15",
606+
})
607+
fmt.Fprint(w, `{
608+
"usageItems": [
609+
{
610+
"date": "2023-08-15",
611+
"product": "Codespaces",
612+
"sku": "Codespaces Linux",
613+
"quantity": 50,
614+
"unitType": "hours",
615+
"pricePerUnit": 0.18,
616+
"grossAmount": 9.0,
617+
"discountAmount": 1.0,
618+
"netAmount": 8.0,
619+
"repositoryName": "user/example"
620+
}
621+
]
622+
}`)
623+
})
624+
625+
ctx := context.Background()
626+
opts := &UsageReportOptions{
627+
Day: Ptr(15),
628+
}
629+
report, _, err := client.Billing.GetUsageReportUser(ctx, "u", opts)
630+
if err != nil {
631+
t.Errorf("Billing.GetUsageReportUser returned error: %v", err)
632+
}
633+
634+
want := &UsageReport{
635+
UsageItems: []*UsageItem{
636+
{
637+
Date: Ptr("2023-08-15"),
638+
Product: Ptr("Codespaces"),
639+
SKU: Ptr("Codespaces Linux"),
640+
Quantity: Ptr(50),
641+
UnitType: Ptr("hours"),
642+
PricePerUnit: Ptr(0.18),
643+
GrossAmount: Ptr(9.0),
644+
DiscountAmount: Ptr(1.0),
645+
NetAmount: Ptr(8.0),
646+
RepositoryName: Ptr("user/example"),
647+
},
648+
},
649+
}
650+
if !cmp.Equal(report, want) {
651+
t.Errorf("Billing.GetUsageReportUser returned %+v, want %+v", report, want)
652+
}
653+
654+
const methodName = "GetUsageReportUser"
655+
testBadOptions(t, methodName, func() (err error) {
656+
_, _, err = client.Billing.GetUsageReportUser(ctx, "\n", nil)
657+
return err
658+
})
659+
660+
testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
661+
got, resp, err := client.Billing.GetUsageReportUser(ctx, "u", nil)
662+
if got != nil {
663+
t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
664+
}
665+
return resp, err
666+
})
667+
}
668+
669+
func TestBillingService_GetUsageReportUser_invalidUser(t *testing.T) {
670+
t.Parallel()
671+
client, _, _ := setup(t)
672+
673+
ctx := context.Background()
674+
_, _, err := client.Billing.GetUsageReportUser(ctx, "%", nil)
675+
testURLParseError(t, err)
676+
}

github/github-accessors.go

Lines changed: 120 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)