Skip to content

Commit 115b51d

Browse files
committed
feat(billing): add shared utility functions for enterprise billing data sources
1 parent 11fc028 commit 115b51d

2 files changed

Lines changed: 558 additions & 0 deletions

File tree

github/util_enterprise_billing.go

Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/url"
7+
"strconv"
8+
9+
"github.com/google/go-github/v83/github"
10+
)
11+
12+
// EnterpriseBillingUsageOptions specifies optional parameters for the
13+
// enterprise billing usage report endpoint.
14+
type EnterpriseBillingUsageOptions struct {
15+
Year *int `url:"year,omitempty"`
16+
Month *int `url:"month,omitempty"`
17+
Day *int `url:"day,omitempty"`
18+
CostCenterID *string `url:"cost_center_id,omitempty"`
19+
}
20+
21+
// EnterprisePremiumRequestUsageOptions specifies optional parameters for the
22+
// enterprise billing premium request usage report endpoint.
23+
type EnterprisePremiumRequestUsageOptions struct {
24+
Year *int `url:"year,omitempty"`
25+
Month *int `url:"month,omitempty"`
26+
Day *int `url:"day,omitempty"`
27+
Organization *string `url:"organization,omitempty"`
28+
User *string `url:"user,omitempty"`
29+
Model *string `url:"model,omitempty"`
30+
Product *string `url:"product,omitempty"`
31+
CostCenterID *string `url:"cost_center_id,omitempty"`
32+
}
33+
34+
// EnterpriseUsageSummaryOptions specifies optional parameters for the
35+
// enterprise billing usage summary endpoint.
36+
type EnterpriseUsageSummaryOptions struct {
37+
Year *int `url:"year,omitempty"`
38+
Month *int `url:"month,omitempty"`
39+
Day *int `url:"day,omitempty"`
40+
Organization *string `url:"organization,omitempty"`
41+
Repository *string `url:"repository,omitempty"`
42+
Product *string `url:"product,omitempty"`
43+
SKU *string `url:"sku,omitempty"`
44+
CostCenterID *string `url:"cost_center_id,omitempty"`
45+
}
46+
47+
// EnterprisePremiumRequestUsageReport represents the enterprise-level
48+
// premium request usage report response.
49+
type EnterprisePremiumRequestUsageReport struct {
50+
TimePeriod github.PremiumRequestUsageTimePeriod `json:"timePeriod"`
51+
Enterprise *string `json:"enterprise,omitempty"`
52+
UsageItems []*github.PremiumRequestUsageItem `json:"usageItems"`
53+
}
54+
55+
// EnterpriseUsageSummaryItem represents a single usage line item in an
56+
// enterprise billing usage summary report.
57+
type EnterpriseUsageSummaryItem struct {
58+
Product string `json:"product"`
59+
SKU string `json:"sku"`
60+
UnitType string `json:"unitType"`
61+
PricePerUnit float64 `json:"pricePerUnit"`
62+
GrossQuantity float64 `json:"grossQuantity"`
63+
GrossAmount float64 `json:"grossAmount"`
64+
DiscountQuantity float64 `json:"discountQuantity"`
65+
DiscountAmount float64 `json:"discountAmount"`
66+
NetQuantity float64 `json:"netQuantity"`
67+
NetAmount float64 `json:"netAmount"`
68+
}
69+
70+
// EnterpriseUsageSummaryReport represents the enterprise-level billing
71+
// usage summary report response.
72+
type EnterpriseUsageSummaryReport struct {
73+
TimePeriod github.PremiumRequestUsageTimePeriod `json:"timePeriod"`
74+
Enterprise *string `json:"enterprise,omitempty"`
75+
UsageItems []*EnterpriseUsageSummaryItem `json:"usageItems"`
76+
}
77+
78+
// buildQueryURL constructs a URL with non-empty query parameters.
79+
func buildQueryURL(base string, params map[string]string) string {
80+
values := url.Values{}
81+
for key, val := range params {
82+
if val != "" {
83+
values.Set(key, val)
84+
}
85+
}
86+
87+
if len(values) == 0 {
88+
return base
89+
}
90+
91+
return base + "?" + values.Encode()
92+
}
93+
94+
// intToString converts an int to its string representation.
95+
// Returns an empty string if the value is zero.
96+
func intToString(value int) string {
97+
if value == 0 {
98+
return ""
99+
}
100+
return strconv.Itoa(value)
101+
}
102+
103+
// getEnterpriseBillingUsage fetches the billing usage report for an enterprise.
104+
func getEnterpriseBillingUsage(ctx context.Context, client *github.Client, enterprise string, opts *EnterpriseBillingUsageOptions) (*github.UsageReport, error) {
105+
urlPath := fmt.Sprintf("enterprises/%s/settings/billing/usage", enterprise)
106+
107+
params := map[string]string{}
108+
if opts != nil {
109+
if opts.Year != nil {
110+
params["year"] = strconv.Itoa(*opts.Year)
111+
}
112+
if opts.Month != nil {
113+
params["month"] = strconv.Itoa(*opts.Month)
114+
}
115+
if opts.Day != nil {
116+
params["day"] = strconv.Itoa(*opts.Day)
117+
}
118+
if opts.CostCenterID != nil {
119+
params["cost_center_id"] = *opts.CostCenterID
120+
}
121+
}
122+
123+
urlPath = buildQueryURL(urlPath, params)
124+
125+
req, err := client.NewRequest("GET", urlPath, nil)
126+
if err != nil {
127+
return nil, err
128+
}
129+
130+
report := new(github.UsageReport)
131+
_, err = client.Do(ctx, req, report)
132+
if err != nil {
133+
return nil, err
134+
}
135+
136+
return report, nil
137+
}
138+
139+
// getEnterprisePremiumRequestUsage fetches the billing premium request usage report for an enterprise.
140+
func getEnterprisePremiumRequestUsage(ctx context.Context, client *github.Client, enterprise string, opts *EnterprisePremiumRequestUsageOptions) (*EnterprisePremiumRequestUsageReport, error) {
141+
urlPath := fmt.Sprintf("enterprises/%s/settings/billing/premium_request/usage", enterprise)
142+
143+
params := map[string]string{}
144+
if opts != nil {
145+
if opts.Year != nil {
146+
params["year"] = strconv.Itoa(*opts.Year)
147+
}
148+
if opts.Month != nil {
149+
params["month"] = strconv.Itoa(*opts.Month)
150+
}
151+
if opts.Day != nil {
152+
params["day"] = strconv.Itoa(*opts.Day)
153+
}
154+
if opts.Organization != nil {
155+
params["organization"] = *opts.Organization
156+
}
157+
if opts.User != nil {
158+
params["user"] = *opts.User
159+
}
160+
if opts.Model != nil {
161+
params["model"] = *opts.Model
162+
}
163+
if opts.Product != nil {
164+
params["product"] = *opts.Product
165+
}
166+
if opts.CostCenterID != nil {
167+
params["cost_center_id"] = *opts.CostCenterID
168+
}
169+
}
170+
171+
urlPath = buildQueryURL(urlPath, params)
172+
173+
req, err := client.NewRequest("GET", urlPath, nil)
174+
if err != nil {
175+
return nil, err
176+
}
177+
178+
report := new(EnterprisePremiumRequestUsageReport)
179+
_, err = client.Do(ctx, req, report)
180+
if err != nil {
181+
return nil, err
182+
}
183+
184+
return report, nil
185+
}
186+
187+
// getEnterpriseUsageSummary fetches the billing usage summary for an enterprise.
188+
func getEnterpriseUsageSummary(ctx context.Context, client *github.Client, enterprise string, opts *EnterpriseUsageSummaryOptions) (*EnterpriseUsageSummaryReport, error) {
189+
urlPath := fmt.Sprintf("enterprises/%s/settings/billing/usage/summary", enterprise)
190+
191+
params := map[string]string{}
192+
if opts != nil {
193+
if opts.Year != nil {
194+
params["year"] = strconv.Itoa(*opts.Year)
195+
}
196+
if opts.Month != nil {
197+
params["month"] = strconv.Itoa(*opts.Month)
198+
}
199+
if opts.Day != nil {
200+
params["day"] = strconv.Itoa(*opts.Day)
201+
}
202+
if opts.Organization != nil {
203+
params["organization"] = *opts.Organization
204+
}
205+
if opts.Repository != nil {
206+
params["repository"] = *opts.Repository
207+
}
208+
if opts.Product != nil {
209+
params["product"] = *opts.Product
210+
}
211+
if opts.SKU != nil {
212+
params["sku"] = *opts.SKU
213+
}
214+
if opts.CostCenterID != nil {
215+
params["cost_center_id"] = *opts.CostCenterID
216+
}
217+
}
218+
219+
urlPath = buildQueryURL(urlPath, params)
220+
221+
req, err := client.NewRequest("GET", urlPath, nil)
222+
if err != nil {
223+
return nil, err
224+
}
225+
226+
report := new(EnterpriseUsageSummaryReport)
227+
_, err = client.Do(ctx, req, report)
228+
if err != nil {
229+
return nil, err
230+
}
231+
232+
return report, nil
233+
}
234+
235+
// flattenUsageItems converts billing usage items to a Terraform state-compatible format.
236+
func flattenUsageItems(items []*github.UsageItem) []map[string]any {
237+
result := make([]map[string]any, len(items))
238+
for idx, item := range items {
239+
result[idx] = map[string]any{
240+
"date": item.Date,
241+
"product": item.Product,
242+
"sku": item.SKU,
243+
"quantity": item.Quantity,
244+
"unit_type": item.UnitType,
245+
"price_per_unit": item.PricePerUnit,
246+
"gross_amount": item.GrossAmount,
247+
"discount_amount": item.DiscountAmount,
248+
"net_amount": item.NetAmount,
249+
"organization_name": item.OrganizationName,
250+
"repository_name": item.RepositoryName,
251+
}
252+
}
253+
return result
254+
}
255+
256+
// flattenPremiumRequestUsageItems converts premium request usage items to a Terraform state-compatible format.
257+
func flattenPremiumRequestUsageItems(items []*github.PremiumRequestUsageItem) []map[string]any {
258+
result := make([]map[string]any, len(items))
259+
for idx, item := range items {
260+
result[idx] = map[string]any{
261+
"product": item.Product,
262+
"sku": item.SKU,
263+
"model": item.Model,
264+
"unit_type": item.UnitType,
265+
"price_per_unit": item.PricePerUnit,
266+
"gross_quantity": item.GrossQuantity,
267+
"gross_amount": item.GrossAmount,
268+
"discount_quantity": item.DiscountQuantity,
269+
"discount_amount": item.DiscountAmount,
270+
"net_quantity": item.NetQuantity,
271+
"net_amount": item.NetAmount,
272+
}
273+
}
274+
return result
275+
}
276+
277+
// flattenUsageSummaryItems converts usage summary items to a Terraform state-compatible format.
278+
func flattenUsageSummaryItems(items []*EnterpriseUsageSummaryItem) []map[string]any {
279+
result := make([]map[string]any, len(items))
280+
for idx, item := range items {
281+
result[idx] = map[string]any{
282+
"product": item.Product,
283+
"sku": item.SKU,
284+
"unit_type": item.UnitType,
285+
"price_per_unit": item.PricePerUnit,
286+
"gross_quantity": item.GrossQuantity,
287+
"gross_amount": item.GrossAmount,
288+
"discount_quantity": item.DiscountQuantity,
289+
"discount_amount": item.DiscountAmount,
290+
"net_quantity": item.NetQuantity,
291+
"net_amount": item.NetAmount,
292+
}
293+
}
294+
return result
295+
}
296+
297+
// flattenTimePeriod converts a PremiumRequestUsageTimePeriod to a Terraform state-compatible format.
298+
func flattenTimePeriod(timePeriod github.PremiumRequestUsageTimePeriod) []map[string]any {
299+
result := map[string]any{
300+
"year": timePeriod.Year,
301+
}
302+
if timePeriod.Month != nil {
303+
result["month"] = *timePeriod.Month
304+
} else {
305+
result["month"] = 0
306+
}
307+
if timePeriod.Day != nil {
308+
result["day"] = *timePeriod.Day
309+
} else {
310+
result["day"] = 0
311+
}
312+
return []map[string]any{result}
313+
}

0 commit comments

Comments
 (0)