diff --git a/github/enterprise_billing_cost_centers.go b/github/enterprise_billing_cost_centers.go new file mode 100644 index 00000000000..681bf9ec000 --- /dev/null +++ b/github/enterprise_billing_cost_centers.go @@ -0,0 +1,232 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// CostCenter represents an enterprise cost center. +type CostCenter struct { + ID string `json:"id"` + Name string `json:"name"` + Resources []*CostCenterResource `json:"resources"` + State *string `json:"state,omitempty"` + AzureSubscription *string `json:"azure_subscription,omitempty"` +} + +// CostCenterResource represents a resource assigned to a cost center. +type CostCenterResource struct { + Type string `json:"type"` + Name string `json:"name"` +} + +// CostCenters represents a list of cost centers. +type CostCenters struct { + CostCenters []*CostCenter `json:"costCenters,omitempty"` +} + +// CostCenterListOptions specifies optional parameters to the EnterpriseService.ListCostCenters method. +type CostCenterListOptions struct { + State *string `url:"state,omitempty"` +} + +// CostCenterRequest represents a request to create or update a cost center. +type CostCenterRequest struct { + Name *string `json:"name,omitempty"` +} + +// CostCenterResourceRequest represents a request to add or remove resources from a cost center. +type CostCenterResourceRequest struct { + Users []string `json:"users,omitempty"` + Organizations []string `json:"organizations,omitempty"` + Repositories []string `json:"repositories,omitempty"` +} + +// CostCenterAddResourceResponse represents a response from adding resources to a cost center. +type CostCenterAddResourceResponse struct { + Message *string `json:"message,omitempty"` + ReassignedResources []*ReassignedResource `json:"reassigned_resources,omitempty"` +} + +// ReassignedResource represents a resource that was reassigned from another cost center. +type ReassignedResource struct { + ResourceType *string `json:"resource_type,omitempty"` + Name *string `json:"name,omitempty"` + PreviousCostCenter *string `json:"previous_cost_center,omitempty"` +} + +// CostCenterRemoveResourceResponse represents a response from removing resources from a cost center. +type CostCenterRemoveResourceResponse struct { + Message *string `json:"message,omitempty"` +} + +// CostCenterDeleteResponse represents a response from deleting a cost center. +type CostCenterDeleteResponse struct { + Message *string `json:"message,omitempty"` + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + CostCenterState *string `json:"costCenterState,omitempty"` +} + +// ListCostCenters lists all cost centers for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/billing#get-all-cost-centers-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/settings/billing/cost-centers +func (s *EnterpriseService) ListCostCenters(ctx context.Context, enterprise string, opts *CostCenterListOptions) (*CostCenters, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/cost-centers", enterprise) + 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 + } + + costCenters := &CostCenters{} + resp, err := s.client.Do(ctx, req, costCenters) + if err != nil { + return nil, resp, err + } + + return costCenters, resp, nil +} + +// CreateCostCenter creates a new cost center for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/billing#create-a-new-cost-center +// +//meta:operation POST /enterprises/{enterprise}/settings/billing/cost-centers +func (s *EnterpriseService) CreateCostCenter(ctx context.Context, enterprise string, costCenter CostCenterRequest) (*CostCenter, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/cost-centers", enterprise) + + req, err := s.client.NewRequest("POST", u, costCenter) + if err != nil { + return nil, nil, err + } + + result := &CostCenter{} + resp, err := s.client.Do(ctx, req, result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// GetCostCenter gets a cost center by ID for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/billing#get-a-cost-center-by-id +// +//meta:operation GET /enterprises/{enterprise}/settings/billing/cost-centers/{cost_center_id} +func (s *EnterpriseService) GetCostCenter(ctx context.Context, enterprise, costCenterID string) (*CostCenter, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/cost-centers/%v", enterprise, costCenterID) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + costCenter := &CostCenter{} + resp, err := s.client.Do(ctx, req, costCenter) + if err != nil { + return nil, resp, err + } + + return costCenter, resp, nil +} + +// UpdateCostCenter updates the name of a cost center. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/billing#update-a-cost-center-name +// +//meta:operation PATCH /enterprises/{enterprise}/settings/billing/cost-centers/{cost_center_id} +func (s *EnterpriseService) UpdateCostCenter(ctx context.Context, enterprise, costCenterID string, costCenter CostCenterRequest) (*CostCenter, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/cost-centers/%v", enterprise, costCenterID) + + req, err := s.client.NewRequest("PATCH", u, costCenter) + if err != nil { + return nil, nil, err + } + + result := &CostCenter{} + resp, err := s.client.Do(ctx, req, result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// DeleteCostCenter deletes a cost center. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/billing#delete-a-cost-center +// +//meta:operation DELETE /enterprises/{enterprise}/settings/billing/cost-centers/{cost_center_id} +func (s *EnterpriseService) DeleteCostCenter(ctx context.Context, enterprise, costCenterID string) (*CostCenterDeleteResponse, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/cost-centers/%v", enterprise, costCenterID) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, nil, err + } + + result := &CostCenterDeleteResponse{} + resp, err := s.client.Do(ctx, req, result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// AddResourcesToCostCenter adds resources to a cost center. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/billing#add-resources-to-a-cost-center +// +//meta:operation POST /enterprises/{enterprise}/settings/billing/cost-centers/{cost_center_id}/resource +func (s *EnterpriseService) AddResourcesToCostCenter(ctx context.Context, enterprise, costCenterID string, resources CostCenterResourceRequest) (*CostCenterAddResourceResponse, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/cost-centers/%v/resource", enterprise, costCenterID) + + req, err := s.client.NewRequest("POST", u, resources) + if err != nil { + return nil, nil, err + } + + result := &CostCenterAddResourceResponse{} + resp, err := s.client.Do(ctx, req, result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// RemoveResourcesFromCostCenter removes resources from a cost center. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/billing#remove-resources-from-a-cost-center +// +//meta:operation DELETE /enterprises/{enterprise}/settings/billing/cost-centers/{cost_center_id}/resource +func (s *EnterpriseService) RemoveResourcesFromCostCenter(ctx context.Context, enterprise, costCenterID string, resources CostCenterResourceRequest) (*CostCenterRemoveResourceResponse, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/cost-centers/%v/resource", enterprise, costCenterID) + + req, err := s.client.NewRequest("DELETE", u, resources) + if err != nil { + return nil, nil, err + } + + result := &CostCenterRemoveResourceResponse{} + resp, err := s.client.Do(ctx, req, result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} diff --git a/github/enterprise_billing_cost_centers_test.go b/github/enterprise_billing_cost_centers_test.go new file mode 100644 index 00000000000..d450a701eb5 --- /dev/null +++ b/github/enterprise_billing_cost_centers_test.go @@ -0,0 +1,637 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_ListCostCenters(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/settings/billing/cost-centers", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "state": "active", + }) + fmt.Fprint(w, `{ + "costCenters": [ + { + "id": "2eeb8ffe-6903-11ee-8c99-0242ac120002", + "name": "Cost Center Name", + "state": "active", + "azure_subscription": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "resources": [ + { + "type": "User", + "name": "Monalisa" + }, + { + "type": "Repo", + "name": "octocat/hello-world" + } + ] + }, + { + "id": "3ffb9ffe-6903-11ee-8c99-0242ac120003", + "name": "Another Cost Center", + "state": "active", + "resources": [ + { + "type": "User", + "name": "Octocat" + } + ] + } + ] + }`) + }) + + ctx := t.Context() + opts := &CostCenterListOptions{ + State: Ptr("active"), + } + costCenters, _, err := client.Enterprise.ListCostCenters(ctx, "e", opts) + if err != nil { + t.Errorf("Enterprise.ListCostCenters returned error: %v", err) + } + + want := &CostCenters{ + CostCenters: []*CostCenter{ + { + ID: "2eeb8ffe-6903-11ee-8c99-0242ac120002", + Name: "Cost Center Name", + State: Ptr("active"), + AzureSubscription: Ptr("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"), + Resources: []*CostCenterResource{ + { + Type: "User", + Name: "Monalisa", + }, + { + Type: "Repo", + Name: "octocat/hello-world", + }, + }, + }, + { + ID: "3ffb9ffe-6903-11ee-8c99-0242ac120003", + Name: "Another Cost Center", + State: Ptr("active"), + Resources: []*CostCenterResource{ + { + Type: "User", + Name: "Octocat", + }, + }, + }, + }, + } + if !cmp.Equal(costCenters, want) { + t.Errorf("Enterprise.ListCostCenters returned %+v, want %+v", costCenters, want) + } + + const methodName = "ListCostCenters" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListCostCenters(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListCostCenters(ctx, "e", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_ListCostCenters_invalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.ListCostCenters(ctx, "%", nil) + testURLParseError(t, err) +} + +func TestEnterpriseService_CreateCostCenter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/settings/billing/cost-centers", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testBody(t, r, `{"name":"Engineering Team"}`+"\n") + fmt.Fprint(w, `{ + "id": "abc123", + "name": "Engineering Team", + "resources": [] + }`) + }) + + ctx := t.Context() + req := CostCenterRequest{ + Name: Ptr("Engineering Team"), + } + costCenter, _, err := client.Enterprise.CreateCostCenter(ctx, "e", req) + if err != nil { + t.Errorf("Enterprise.CreateCostCenter returned error: %v", err) + } + + want := &CostCenter{ + ID: "abc123", + Name: "Engineering Team", + Resources: []*CostCenterResource{}, + } + if !cmp.Equal(costCenter, want) { + t.Errorf("Enterprise.CreateCostCenter returned %+v, want %+v", costCenter, want) + } + + const methodName = "CreateCostCenter" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.CreateCostCenter(ctx, "\n", req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.CreateCostCenter(ctx, "e", req) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_CreateCostCenter_invalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.CreateCostCenter(ctx, "%", CostCenterRequest{}) + testURLParseError(t, err) +} + +func TestEnterpriseService_GetCostCenter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/settings/billing/cost-centers/2eeb8ffe-6903-11ee-8c99-0242ac120002", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": "2eeb8ffe-6903-11ee-8c99-0242ac120002", + "name": "Cost Center Name", + "state": "active", + "azure_subscription": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "resources": [ + { + "type": "User", + "name": "Monalisa" + }, + { + "type": "Repo", + "name": "octocat/hello-world" + } + ] + }`) + }) + + ctx := t.Context() + costCenter, _, err := client.Enterprise.GetCostCenter(ctx, "e", "2eeb8ffe-6903-11ee-8c99-0242ac120002") + if err != nil { + t.Errorf("Enterprise.GetCostCenter returned error: %v", err) + } + + want := &CostCenter{ + ID: "2eeb8ffe-6903-11ee-8c99-0242ac120002", + Name: "Cost Center Name", + State: Ptr("active"), + AzureSubscription: Ptr("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"), + Resources: []*CostCenterResource{ + { + Type: "User", + Name: "Monalisa", + }, + { + Type: "Repo", + Name: "octocat/hello-world", + }, + }, + } + if !cmp.Equal(costCenter, want) { + t.Errorf("Enterprise.GetCostCenter returned %+v, want %+v", costCenter, want) + } + + const methodName = "GetCostCenter" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetCostCenter(ctx, "\n", "2eeb8ffe-6903-11ee-8c99-0242ac120002") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetCostCenter(ctx, "e", "2eeb8ffe-6903-11ee-8c99-0242ac120002") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetCostCenter_invalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.GetCostCenter(ctx, "%", "2eeb8ffe-6903-11ee-8c99-0242ac120002") + testURLParseError(t, err) +} + +func TestEnterpriseService_UpdateCostCenter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/settings/billing/cost-centers/2eeb8ffe-6903-11ee-8c99-0242ac120002", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testBody(t, r, `{"name":"Updated Cost Center Name"}`+"\n") + fmt.Fprint(w, `{ + "id": "2eeb8ffe-6903-11ee-8c99-0242ac120002", + "name": "Updated Cost Center Name", + "state": "active", + "azure_subscription": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "resources": [ + { + "type": "User", + "name": "Monalisa" + }, + { + "type": "Repo", + "name": "octocat/hello-world" + } + ] + }`) + }) + + ctx := t.Context() + req := CostCenterRequest{ + Name: Ptr("Updated Cost Center Name"), + } + costCenter, _, err := client.Enterprise.UpdateCostCenter(ctx, "e", "2eeb8ffe-6903-11ee-8c99-0242ac120002", req) + if err != nil { + t.Errorf("Enterprise.UpdateCostCenter returned error: %v", err) + } + + want := &CostCenter{ + ID: "2eeb8ffe-6903-11ee-8c99-0242ac120002", + Name: "Updated Cost Center Name", + State: Ptr("active"), + AzureSubscription: Ptr("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"), + Resources: []*CostCenterResource{ + { + Type: "User", + Name: "Monalisa", + }, + { + Type: "Repo", + Name: "octocat/hello-world", + }, + }, + } + if !cmp.Equal(costCenter, want) { + t.Errorf("Enterprise.UpdateCostCenter returned %+v, want %+v", costCenter, want) + } + + const methodName = "UpdateCostCenter" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.UpdateCostCenter(ctx, "\n", "2eeb8ffe-6903-11ee-8c99-0242ac120002", req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.UpdateCostCenter(ctx, "e", "2eeb8ffe-6903-11ee-8c99-0242ac120002", req) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_UpdateCostCenter_invalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.UpdateCostCenter(ctx, "%", "2eeb8ffe-6903-11ee-8c99-0242ac120002", CostCenterRequest{}) + testURLParseError(t, err) +} + +func TestEnterpriseService_DeleteCostCenter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/settings/billing/cost-centers/2eeb8ffe-6903-11ee-8c99-0242ac120002", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + fmt.Fprint(w, `{ + "message": "Cost center successfully deleted.", + "id": "2eeb8ffe-6903-11ee-8c99-0242ac120002", + "name": "Engineering Team", + "costCenterState": "CostCenterArchived" + }`) + }) + + ctx := t.Context() + result, _, err := client.Enterprise.DeleteCostCenter(ctx, "e", "2eeb8ffe-6903-11ee-8c99-0242ac120002") + if err != nil { + t.Errorf("Enterprise.DeleteCostCenter returned error: %v", err) + } + + want := &CostCenterDeleteResponse{ + Message: Ptr("Cost center successfully deleted."), + ID: Ptr("2eeb8ffe-6903-11ee-8c99-0242ac120002"), + Name: Ptr("Engineering Team"), + CostCenterState: Ptr("CostCenterArchived"), + } + if !cmp.Equal(result, want) { + t.Errorf("Enterprise.DeleteCostCenter returned %+v, want %+v", result, want) + } + + const methodName = "DeleteCostCenter" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.DeleteCostCenter(ctx, "\n", "2eeb8ffe-6903-11ee-8c99-0242ac120002") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.DeleteCostCenter(ctx, "e", "2eeb8ffe-6903-11ee-8c99-0242ac120002") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_DeleteCostCenter_invalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.DeleteCostCenter(ctx, "%", "2eeb8ffe-6903-11ee-8c99-0242ac120002") + testURLParseError(t, err) +} + +func TestEnterpriseService_AddResourcesToCostCenter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/settings/billing/cost-centers/2eeb8ffe-6903-11ee-8c99-0242ac120002/resource", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testBody(t, r, `{"users":["monalisa"]}`+"\n") + fmt.Fprint(w, `{ + "message": "Resources successfully added to the cost center.", + "reassigned_resources": [ + { + "resource_type": "user", + "name": "monalisa", + "previous_cost_center": "old-cost-center" + }, + { + "resource_type": "organization", + "name": "octo-org", + "previous_cost_center": "another-cost-center" + }, + { + "resource_type": "repository", + "name": "octo-repo", + "previous_cost_center": "yet-another-cost-center" + } + ] + }`) + }) + + ctx := t.Context() + req := CostCenterResourceRequest{ + Users: []string{"monalisa"}, + } + result, _, err := client.Enterprise.AddResourcesToCostCenter(ctx, "e", "2eeb8ffe-6903-11ee-8c99-0242ac120002", req) + if err != nil { + t.Errorf("Enterprise.AddResourcesToCostCenter returned error: %v", err) + } + + want := &CostCenterAddResourceResponse{ + Message: Ptr("Resources successfully added to the cost center."), + ReassignedResources: []*ReassignedResource{ + { + ResourceType: Ptr("user"), + Name: Ptr("monalisa"), + PreviousCostCenter: Ptr("old-cost-center"), + }, + { + ResourceType: Ptr("organization"), + Name: Ptr("octo-org"), + PreviousCostCenter: Ptr("another-cost-center"), + }, + { + ResourceType: Ptr("repository"), + Name: Ptr("octo-repo"), + PreviousCostCenter: Ptr("yet-another-cost-center"), + }, + }, + } + if !cmp.Equal(result, want) { + t.Errorf("Enterprise.AddResourcesToCostCenter returned %+v, want %+v", result, want) + } + + const methodName = "AddResourcesToCostCenter" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.AddResourcesToCostCenter(ctx, "\n", "2eeb8ffe-6903-11ee-8c99-0242ac120002", req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.AddResourcesToCostCenter(ctx, "e", "2eeb8ffe-6903-11ee-8c99-0242ac120002", req) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_AddResourcesToCostCenter_invalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.AddResourcesToCostCenter(ctx, "%", "2eeb8ffe-6903-11ee-8c99-0242ac120002", CostCenterResourceRequest{}) + testURLParseError(t, err) +} + +func TestEnterpriseService_RemoveResourcesFromCostCenter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/settings/billing/cost-centers/2eeb8ffe-6903-11ee-8c99-0242ac120002/resource", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testBody(t, r, `{"users":["monalisa"]}`+"\n") + fmt.Fprint(w, `{ + "message": "Resources successfully removed from the cost center." + }`) + }) + + ctx := t.Context() + req := CostCenterResourceRequest{ + Users: []string{"monalisa"}, + } + result, _, err := client.Enterprise.RemoveResourcesFromCostCenter(ctx, "e", "2eeb8ffe-6903-11ee-8c99-0242ac120002", req) + if err != nil { + t.Errorf("Enterprise.RemoveResourcesFromCostCenter returned error: %v", err) + } + + want := &CostCenterRemoveResourceResponse{ + Message: Ptr("Resources successfully removed from the cost center."), + } + if !cmp.Equal(result, want) { + t.Errorf("Enterprise.RemoveResourcesFromCostCenter returned %+v, want %+v", result, want) + } + + const methodName = "RemoveResourcesFromCostCenter" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.RemoveResourcesFromCostCenter(ctx, "\n", "2eeb8ffe-6903-11ee-8c99-0242ac120002", req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.RemoveResourcesFromCostCenter(ctx, "e", "2eeb8ffe-6903-11ee-8c99-0242ac120002", req) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_RemoveResourcesFromCostCenter_invalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.RemoveResourcesFromCostCenter(ctx, "%", "2eeb8ffe-6903-11ee-8c99-0242ac120002", CostCenterResourceRequest{}) + testURLParseError(t, err) +} + +func TestCostCenter_Marshal(t *testing.T) { + t.Parallel() + testJSONMarshal(t, &CostCenter{}, `{"id":"","name":"","resources":null}`) + + u := &CostCenter{ + ID: "1", + Name: "Engineering", + State: Ptr("active"), + AzureSubscription: Ptr("sub-123"), + Resources: []*CostCenterResource{ + { + Type: "user", + Name: "octocat", + }, + }, + } + + want := `{ + "id": "1", + "name": "Engineering", + "state": "active", + "azure_subscription": "sub-123", + "resources": [ + { + "type": "user", + "name": "octocat" + } + ] + }` + + testJSONMarshal(t, u, want) +} + +func TestCostCenterResource_Marshal(t *testing.T) { + t.Parallel() + testJSONMarshal(t, &CostCenterResource{}, `{"type":"","name":""}`) + + u := &CostCenterResource{ + Type: "user", + Name: "octocat", + } + + want := `{ + "type": "user", + "name": "octocat" + }` + + testJSONMarshal(t, u, want) +} + +func TestCostCenters_Marshal(t *testing.T) { + t.Parallel() + testJSONMarshal(t, &CostCenters{}, "{}") + + u := &CostCenters{ + CostCenters: []*CostCenter{ + { + ID: "1", + Name: "Engineering", + Resources: []*CostCenterResource{}, + State: Ptr("active"), + }, + }, + } + + want := `{ + "costCenters": [ + { + "id": "1", + "name": "Engineering", + "resources": [], + "state": "active" + } + ] + }` + + testJSONMarshal(t, u, want) +} + +func TestCostCenterRequest_Marshal(t *testing.T) { + t.Parallel() + testJSONMarshal(t, &CostCenterRequest{}, "{}") + + u := &CostCenterRequest{ + Name: Ptr("Engineering"), + } + + want := `{ + "name": "Engineering" + }` + + testJSONMarshal(t, u, want) +} + +func TestCostCenterResourceRequest_Marshal(t *testing.T) { + t.Parallel() + testJSONMarshal(t, &CostCenterResourceRequest{}, "{}") + + u := &CostCenterResourceRequest{ + Users: []string{"octocat"}, + Organizations: []string{"github"}, + Repositories: []string{"github/go-github"}, + } + + want := `{ + "users": ["octocat"], + "organizations": ["github"], + "repositories": ["github/go-github"] + }` + + testJSONMarshal(t, u, want) +} diff --git a/github/github-accessors.go b/github/github-accessors.go index 7844b5b36d0..c32e2159611 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -6062,6 +6062,86 @@ func (c *CopilotSeatDetails) GetUpdatedAt() Timestamp { return *c.UpdatedAt } +// GetAzureSubscription returns the AzureSubscription field if it's non-nil, zero value otherwise. +func (c *CostCenter) GetAzureSubscription() string { + if c == nil || c.AzureSubscription == nil { + return "" + } + return *c.AzureSubscription +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (c *CostCenter) GetState() string { + if c == nil || c.State == nil { + return "" + } + return *c.State +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (c *CostCenterAddResourceResponse) GetMessage() string { + if c == nil || c.Message == nil { + return "" + } + return *c.Message +} + +// GetCostCenterState returns the CostCenterState field if it's non-nil, zero value otherwise. +func (c *CostCenterDeleteResponse) GetCostCenterState() string { + if c == nil || c.CostCenterState == nil { + return "" + } + return *c.CostCenterState +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *CostCenterDeleteResponse) GetID() string { + if c == nil || c.ID == nil { + return "" + } + return *c.ID +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (c *CostCenterDeleteResponse) GetMessage() string { + if c == nil || c.Message == nil { + return "" + } + return *c.Message +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *CostCenterDeleteResponse) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (c *CostCenterListOptions) GetState() string { + if c == nil || c.State == nil { + return "" + } + return *c.State +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (c *CostCenterRemoveResourceResponse) GetMessage() string { + if c == nil || c.Message == nil { + return "" + } + return *c.Message +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *CostCenterRequest) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + // GetCompletedAt returns the CompletedAt field if it's non-nil, zero value otherwise. func (c *CreateCheckRunOptions) GetCompletedAt() Timestamp { if c == nil || c.CompletedAt == nil { @@ -21966,6 +22046,30 @@ func (r *Reactions) GetURL() string { return *r.URL } +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (r *ReassignedResource) GetName() string { + if r == nil || r.Name == nil { + return "" + } + return *r.Name +} + +// GetPreviousCostCenter returns the PreviousCostCenter field if it's non-nil, zero value otherwise. +func (r *ReassignedResource) GetPreviousCostCenter() string { + if r == nil || r.PreviousCostCenter == nil { + return "" + } + return *r.PreviousCostCenter +} + +// GetResourceType returns the ResourceType field if it's non-nil, zero value otherwise. +func (r *ReassignedResource) GetResourceType() string { + if r == nil || r.ResourceType == nil { + return "" + } + return *r.ResourceType +} + // GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. func (r *Reference) GetNodeID() string { if r == nil || r.NodeID == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index c6ece7ee50a..d3bef6e4a9d 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -7908,6 +7908,116 @@ func TestCopilotSeatDetails_GetUpdatedAt(tt *testing.T) { c.GetUpdatedAt() } +func TestCostCenter_GetAzureSubscription(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CostCenter{AzureSubscription: &zeroValue} + c.GetAzureSubscription() + c = &CostCenter{} + c.GetAzureSubscription() + c = nil + c.GetAzureSubscription() +} + +func TestCostCenter_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CostCenter{State: &zeroValue} + c.GetState() + c = &CostCenter{} + c.GetState() + c = nil + c.GetState() +} + +func TestCostCenterAddResourceResponse_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CostCenterAddResourceResponse{Message: &zeroValue} + c.GetMessage() + c = &CostCenterAddResourceResponse{} + c.GetMessage() + c = nil + c.GetMessage() +} + +func TestCostCenterDeleteResponse_GetCostCenterState(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CostCenterDeleteResponse{CostCenterState: &zeroValue} + c.GetCostCenterState() + c = &CostCenterDeleteResponse{} + c.GetCostCenterState() + c = nil + c.GetCostCenterState() +} + +func TestCostCenterDeleteResponse_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CostCenterDeleteResponse{ID: &zeroValue} + c.GetID() + c = &CostCenterDeleteResponse{} + c.GetID() + c = nil + c.GetID() +} + +func TestCostCenterDeleteResponse_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CostCenterDeleteResponse{Message: &zeroValue} + c.GetMessage() + c = &CostCenterDeleteResponse{} + c.GetMessage() + c = nil + c.GetMessage() +} + +func TestCostCenterDeleteResponse_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CostCenterDeleteResponse{Name: &zeroValue} + c.GetName() + c = &CostCenterDeleteResponse{} + c.GetName() + c = nil + c.GetName() +} + +func TestCostCenterListOptions_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CostCenterListOptions{State: &zeroValue} + c.GetState() + c = &CostCenterListOptions{} + c.GetState() + c = nil + c.GetState() +} + +func TestCostCenterRemoveResourceResponse_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CostCenterRemoveResourceResponse{Message: &zeroValue} + c.GetMessage() + c = &CostCenterRemoveResourceResponse{} + c.GetMessage() + c = nil + c.GetMessage() +} + +func TestCostCenterRequest_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CostCenterRequest{Name: &zeroValue} + c.GetName() + c = &CostCenterRequest{} + c.GetName() + c = nil + c.GetName() +} + func TestCreateCheckRunOptions_GetCompletedAt(tt *testing.T) { tt.Parallel() var zeroValue Timestamp @@ -28369,6 +28479,39 @@ func TestReactions_GetURL(tt *testing.T) { r.GetURL() } +func TestReassignedResource_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReassignedResource{Name: &zeroValue} + r.GetName() + r = &ReassignedResource{} + r.GetName() + r = nil + r.GetName() +} + +func TestReassignedResource_GetPreviousCostCenter(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReassignedResource{PreviousCostCenter: &zeroValue} + r.GetPreviousCostCenter() + r = &ReassignedResource{} + r.GetPreviousCostCenter() + r = nil + r.GetPreviousCostCenter() +} + +func TestReassignedResource_GetResourceType(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReassignedResource{ResourceType: &zeroValue} + r.GetResourceType() + r = &ReassignedResource{} + r.GetResourceType() + r = nil + r.GetResourceType() +} + func TestReference_GetNodeID(tt *testing.T) { tt.Parallel() var zeroValue string