Skip to content

Commit fd0ada9

Browse files
committed
feat(billing): add enterprise billing data sources
Add three new data sources for GitHub enterprise billing: - github_enterprise_billing_usage - github_enterprise_billing_premium_request_usage - github_enterprise_billing_usage_summary
1 parent 115b51d commit fd0ada9

9 files changed

Lines changed: 957 additions & 0 deletions
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
package github
2+
3+
import (
4+
"context"
5+
6+
"github.com/google/go-github/v83/github"
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
10+
)
11+
12+
func dataSourceGithubEnterpriseBillingPremiumRequestUsage() *schema.Resource {
13+
return &schema.Resource{
14+
Description: "Gets a billing premium request usage report for a GitHub enterprise.",
15+
ReadContext: dataSourceGithubEnterpriseBillingPremiumRequestUsageRead,
16+
Schema: map[string]*schema.Schema{
17+
"enterprise_slug": {
18+
Type: schema.TypeString,
19+
Required: true,
20+
Description: "The slug of the enterprise.",
21+
ValidateDiagFunc: validation.ToDiagFunc(validation.StringIsNotEmpty),
22+
},
23+
"year": {
24+
Type: schema.TypeInt,
25+
Optional: true,
26+
Description: "If specified, only return results for a single year.",
27+
ValidateFunc: validation.IntAtLeast(2000),
28+
},
29+
"month": {
30+
Type: schema.TypeInt,
31+
Optional: true,
32+
Description: "If specified, only return results for a single month. Value between 1 and 12.",
33+
ValidateFunc: validation.IntBetween(1, 12),
34+
},
35+
"day": {
36+
Type: schema.TypeInt,
37+
Optional: true,
38+
Description: "If specified, only return results for a single day. Value between 1 and 31.",
39+
ValidateFunc: validation.IntBetween(1, 31),
40+
},
41+
"organization": {
42+
Type: schema.TypeString,
43+
Optional: true,
44+
Description: "The organization name to query usage for.",
45+
},
46+
"user": {
47+
Type: schema.TypeString,
48+
Optional: true,
49+
Description: "The user name to query usage for.",
50+
},
51+
"model": {
52+
Type: schema.TypeString,
53+
Optional: true,
54+
Description: "The model name to query usage for.",
55+
},
56+
"product": {
57+
Type: schema.TypeString,
58+
Optional: true,
59+
Description: "The product name to query usage for.",
60+
},
61+
"cost_center_id": {
62+
Type: schema.TypeString,
63+
Optional: true,
64+
Description: "The ID corresponding to a cost center. Use `none` to target usage not associated to any cost center.",
65+
},
66+
"time_period": {
67+
Type: schema.TypeList,
68+
Computed: true,
69+
Description: "The time period of the report.",
70+
Elem: &schema.Resource{
71+
Schema: map[string]*schema.Schema{
72+
"year": {
73+
Type: schema.TypeInt,
74+
Computed: true,
75+
Description: "The year of the time period.",
76+
},
77+
"month": {
78+
Type: schema.TypeInt,
79+
Computed: true,
80+
Description: "The month of the time period.",
81+
},
82+
"day": {
83+
Type: schema.TypeInt,
84+
Computed: true,
85+
Description: "The day of the time period.",
86+
},
87+
},
88+
},
89+
},
90+
"enterprise": {
91+
Type: schema.TypeString,
92+
Computed: true,
93+
Description: "The enterprise name from the report.",
94+
},
95+
"usage_items": {
96+
Type: schema.TypeList,
97+
Computed: true,
98+
Description: "The list of premium request usage items.",
99+
Elem: &schema.Resource{
100+
Schema: map[string]*schema.Schema{
101+
"product": {
102+
Type: schema.TypeString,
103+
Computed: true,
104+
Description: "The product name.",
105+
},
106+
"sku": {
107+
Type: schema.TypeString,
108+
Computed: true,
109+
Description: "The SKU name.",
110+
},
111+
"model": {
112+
Type: schema.TypeString,
113+
Computed: true,
114+
Description: "The model name.",
115+
},
116+
"unit_type": {
117+
Type: schema.TypeString,
118+
Computed: true,
119+
Description: "The type of unit for the usage.",
120+
},
121+
"price_per_unit": {
122+
Type: schema.TypeFloat,
123+
Computed: true,
124+
Description: "The price per unit of usage.",
125+
},
126+
"gross_quantity": {
127+
Type: schema.TypeFloat,
128+
Computed: true,
129+
Description: "The gross quantity of usage.",
130+
},
131+
"gross_amount": {
132+
Type: schema.TypeFloat,
133+
Computed: true,
134+
Description: "The gross amount of usage.",
135+
},
136+
"discount_quantity": {
137+
Type: schema.TypeFloat,
138+
Computed: true,
139+
Description: "The discount quantity applied.",
140+
},
141+
"discount_amount": {
142+
Type: schema.TypeFloat,
143+
Computed: true,
144+
Description: "The discount amount applied.",
145+
},
146+
"net_quantity": {
147+
Type: schema.TypeFloat,
148+
Computed: true,
149+
Description: "The net quantity after discounts.",
150+
},
151+
"net_amount": {
152+
Type: schema.TypeFloat,
153+
Computed: true,
154+
Description: "The net amount after discounts.",
155+
},
156+
},
157+
},
158+
},
159+
},
160+
}
161+
}
162+
163+
func dataSourceGithubEnterpriseBillingPremiumRequestUsageRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
164+
client := meta.(*Owner).v3client
165+
enterpriseSlug := d.Get("enterprise_slug").(string)
166+
167+
opts := &EnterprisePremiumRequestUsageOptions{}
168+
if yearVal, ok := d.GetOk("year"); ok {
169+
opts.Year = github.Ptr(yearVal.(int))
170+
}
171+
if monthVal, ok := d.GetOk("month"); ok {
172+
opts.Month = github.Ptr(monthVal.(int))
173+
}
174+
if dayVal, ok := d.GetOk("day"); ok {
175+
opts.Day = github.Ptr(dayVal.(int))
176+
}
177+
if orgVal, ok := d.GetOk("organization"); ok {
178+
opts.Organization = github.Ptr(orgVal.(string))
179+
}
180+
if userVal, ok := d.GetOk("user"); ok {
181+
opts.User = github.Ptr(userVal.(string))
182+
}
183+
if modelVal, ok := d.GetOk("model"); ok {
184+
opts.Model = github.Ptr(modelVal.(string))
185+
}
186+
if productVal, ok := d.GetOk("product"); ok {
187+
opts.Product = github.Ptr(productVal.(string))
188+
}
189+
if costCenterID, ok := d.GetOk("cost_center_id"); ok {
190+
opts.CostCenterID = github.Ptr(costCenterID.(string))
191+
}
192+
193+
report, err := getEnterprisePremiumRequestUsage(ctx, client, enterpriseSlug, opts)
194+
if err != nil {
195+
return diag.Errorf("error getting enterprise billing premium request usage for %q: %s", enterpriseSlug, err)
196+
}
197+
198+
id, err := buildID(enterpriseSlug, "billing-premium-request-usage")
199+
if err != nil {
200+
return diag.FromErr(err)
201+
}
202+
d.SetId(id)
203+
204+
if err := d.Set("time_period", flattenTimePeriod(report.TimePeriod)); err != nil {
205+
return diag.FromErr(err)
206+
}
207+
if report.Enterprise != nil {
208+
if err := d.Set("enterprise", *report.Enterprise); err != nil {
209+
return diag.FromErr(err)
210+
}
211+
}
212+
if err := d.Set("usage_items", flattenPremiumRequestUsageItems(report.UsageItems)); err != nil {
213+
return diag.FromErr(err)
214+
}
215+
216+
return nil
217+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package github
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
8+
"github.com/hashicorp/terraform-plugin-testing/knownvalue"
9+
"github.com/hashicorp/terraform-plugin-testing/statecheck"
10+
"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
11+
)
12+
13+
func TestAccGithubEnterpriseBillingPremiumRequestUsage(t *testing.T) {
14+
t.Run("reads premium request usage without error", func(t *testing.T) {
15+
config := fmt.Sprintf(`
16+
data "github_enterprise_billing_premium_request_usage" "test" {
17+
enterprise_slug = "%s"
18+
}
19+
`, testAccConf.enterpriseSlug)
20+
21+
resource.Test(t, resource.TestCase{
22+
PreCheck: func() { skipUnlessMode(t, enterprise) },
23+
ProviderFactories: providerFactories,
24+
Steps: []resource.TestStep{
25+
{
26+
Config: config,
27+
ConfigStateChecks: []statecheck.StateCheck{
28+
statecheck.ExpectKnownValue("data.github_enterprise_billing_premium_request_usage.test",
29+
tfjsonpath.New("enterprise_slug"),
30+
knownvalue.StringExact(testAccConf.enterpriseSlug),
31+
),
32+
},
33+
},
34+
},
35+
})
36+
})
37+
38+
t.Run("reads premium request usage with filters without error", func(t *testing.T) {
39+
config := fmt.Sprintf(`
40+
data "github_enterprise_billing_premium_request_usage" "test" {
41+
enterprise_slug = "%s"
42+
year = 2025
43+
month = 1
44+
}
45+
`, testAccConf.enterpriseSlug)
46+
47+
resource.Test(t, resource.TestCase{
48+
PreCheck: func() { skipUnlessMode(t, enterprise) },
49+
ProviderFactories: providerFactories,
50+
Steps: []resource.TestStep{
51+
{
52+
Config: config,
53+
ConfigStateChecks: []statecheck.StateCheck{
54+
statecheck.ExpectKnownValue("data.github_enterprise_billing_premium_request_usage.test",
55+
tfjsonpath.New("enterprise_slug"),
56+
knownvalue.StringExact(testAccConf.enterpriseSlug),
57+
),
58+
},
59+
},
60+
},
61+
})
62+
})
63+
}

0 commit comments

Comments
 (0)