feat: Add enterprise budgets API#4069
Conversation
Signed-off-by: maishivamhoo123 <maishivamhoo@gmail.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #4069 +/- ##
==========================================
+ Coverage 93.65% 93.67% +0.01%
==========================================
Files 210 211 +1
Lines 19416 19468 +52
==========================================
+ Hits 18184 18236 +52
Misses 1034 1034
Partials 198 198 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
@gmlewis @stevehipwell and @alexandear can you please review this PR? |
gmlewis
left a comment
There was a problem hiding this comment.
Thank you, @maishivamhoo123!
In addition to the comments I made, please see if you can incorporate testBadOptions into your new unit tests for more complete test coverage.
| } | ||
|
|
||
| // Budget represents a GitHub enterprise budget. | ||
| type Budget struct { |
There was a problem hiding this comment.
Since this entire repo makes all its structs "package global", can we please change every struct name that starts with "Budget" to instead start with "EnterpriseBudget" because I think this may help to reduce confusion when people might be thinking of other budgets like Copilot, for instance.
There was a problem hiding this comment.
Can we come up with a different name?
That way we could reuse this struct for organization budgets as well. The endpoints for those aren't added yet, but they are part of issue #3930
Organization budget docs
| budgets := new(EnterpriseBudgets) | ||
| resp, err := s.client.Do(ctx, req, budgets) |
There was a problem hiding this comment.
Let's please stop using this pattern even though it exists a LOT in this repo and I would like to some day change them all... and use the more idiomatic Go style of benefiting from the zero-value:
| budgets := new(EnterpriseBudgets) | |
| resp, err := s.client.Do(ctx, req, budgets) | |
| var budgets *EnterpriseBudgets | |
| resp, err := s.client.Do(ctx, req, &budgets) |
| actionResponse := new(BudgetActionResponse) | ||
| resp, err := s.client.Do(ctx, req, actionResponse) |
| budget := new(Budget) | ||
| resp, err := s.client.Do(ctx, req, budget) |
| actionResponse := new(BudgetActionResponse) | ||
| resp, err := s.client.Do(ctx, req, actionResponse) |
| actionResponse := new(BudgetActionResponse) | ||
| resp, err := s.client.Do(ctx, req, actionResponse) |
| // GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/billing/budgets#create-a-budget | ||
| // | ||
| //meta:operation POST /enterprises/{enterprise}/settings/billing/budgets | ||
| func (s *EnterpriseService) CreateBudget(ctx context.Context, enterprise string, budget *Budget) (*BudgetActionResponse, *Response, error) { |
There was a problem hiding this comment.
budget is required, so should be passed by value:
| func (s *EnterpriseService) CreateBudget(ctx context.Context, enterprise string, budget *Budget) (*BudgetActionResponse, *Response, error) { | |
| func (s *EnterpriseService) CreateBudget(ctx context.Context, enterprise string, budget Budget) (*BudgetActionResponse, *Response, error) { |
There was a problem hiding this comment.
Thank you @gmlewis for pointing out this!.
Sir I have a small doubt as these are required here docs

if I will pass EnterpriseBudgetas value and keep the
this struct like this
type EnterpriseBudget struct {
ID *string `json:"id,omitempty"`
BudgetType *string `json:"budget_type,omitempty"`
BudgetProductSKUs []string `json:"budget_product_skus,omitempty"`
BudgetProductSKU *string `json:"budget_product_sku,omitempty"`
BudgetScope *string `json:"budget_scope,omitempty"`
BudgetEntityName *string `json:"budget_entity_name,omitempty"`
BudgetAmount *int `json:"budget_amount,omitempty"`
PreventFurtherUsage *bool `json:"prevent_further_usage,omitempty"`
BudgetAlerting *EnterpriseBudgetAlerting `json:"budget_alerting,omitempty"`
}
is it ok? can you please explain?
thank you for you time and consideration.
There was a problem hiding this comment.
Yes, you can leave all the field names as they are. I was talking about the struct names only.
| // BudgetAlerting represents alerting settings for a GitHub enterprise budget. | ||
| type BudgetAlerting struct { |
There was a problem hiding this comment.
| // BudgetAlerting represents alerting settings for a GitHub enterprise budget. | |
| type BudgetAlerting struct { | |
| // EnterpriseBudgetAlerting represents alerting settings for a GitHub enterprise budget. | |
| type EnterpriseBudgetAlerting struct { |
| } | ||
|
|
||
| // Budget represents a GitHub enterprise budget. | ||
| type Budget struct { |
There was a problem hiding this comment.
| type Budget struct { | |
| type EnterpriseBudget struct { |
| BudgetAlerting *BudgetAlerting `json:"budget_alerting,omitempty"` | ||
| } | ||
|
|
||
| func (b Budget) String() string { |
There was a problem hiding this comment.
| func (b Budget) String() string { | |
| func (b EnterpriseBudget) String() string { |
| // BudgetActionResponse represents the response when creating, updating, or deleting a budget. | ||
| type BudgetActionResponse struct { |
There was a problem hiding this comment.
| // BudgetActionResponse represents the response when creating, updating, or deleting a budget. | |
| type BudgetActionResponse struct { | |
| // EnterpriseBudgetActionResponse represents the response when creating, updating, or deleting a budget. | |
| type EnterpriseBudgetActionResponse struct { |
|
Thank you @gmlewis, I understand your point, but I am a bit confused here. In the function:
if we pass all the fields are pointers, so they can still be Because of this, I am a bit confused. Should we validate the required fields inside the function, or should the struct be changed to enforce required values? Am I thinking about this correctly, or am I misunderstanding something here? |
There is no need to validate fields within the method. @alexandear was saying that the struct itself should be passed by value into the method since it is required. Some of the fields within the struct are optional, so they remain pointers.
You need to remember that we have auto-generated accessors for the fields themselves so the contents of the struct is a different concept than passing the struct itself into the method. @alexandear is right to pass the struct by value... because if we left it as a pointer, then the method would have to first check if the pointer is Does that make sense to you? |
Fixes #3930
This PR adds support for the newly announced GitHub Enterprise Cloud Billing Budgets API to the EnterpriseService.
It includes the necessary structs, API methods, and unit tests to fully support managing enterprise budgets.