Repository: google/go-github
Pull request #3350 (2024-11-18 13:55:20)

feat: Support Copilot metrics endpoints
Introduces structs and functions for calling the newly GA endpoints for Copilot metrics. See this post from GitHub https://github.blog/changelog/2024-10-30-github-copilot-metrics-api-ga-release-now-available/.

google-cla[bot]: Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this [failed invocation](https://github.com/google/go-github/pull/3350/checks?check_run_id=33140028545) of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/google/go-github/pull/3350?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 92.33%. Comparing base [(`2b8c7fa`)](https://app.codecov.io/gh/google/go-github/commit/2b8c7fa86fcd1cb4a89a44c8e7b5a49fe4be8a45?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) to head [(`fafbaea`)](https://app.codecov.io/gh/google/go-github/commit/fafbaea0c9db384d622d833bb0de60b8b3e01f8d?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google).
> Report is 171 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #3350      +/-   ##
==========================================
- Coverage   97.72%   92.33%   -5.39%     
==========================================
  Files         153      176      +23     
  Lines       13390    15095    +1705     
==========================================
+ Hits        13085    13938     +853     
- Misses        215     1064     +849     
- Partials       90       93       +3     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/google/go-github/pull/3350?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google).

----
🚨 Try these New Features:

- [Flaky Tests Detection](https://docs.codecov.com/docs/test-result-ingestion-beta) - Detect and resolve failed and flaky tests
henriklundstrom: > Thank you, @henriklundstrom ! First, there are a few acronyms that need to be fixed for idiomatic Go naming conventions.
> 
> Then, before continuing my review, it seems like there is a lot of duplication in the new set of structs... are there structs (potential candidates are the ones utilizing `omitempty`) that could possibly be shared, or is there enough distinction to warrant the current number of structs?
> 
> I'm fine if the answer is the latter - I just wanted your opinion to see if you think we could take advantage of simplifying the interface by a bit of sharing (like we do with other structs). But again, if not, no big deal... then no changes necessary.

@gmlewis I've gone through your renaming suggestions and applied them all.

I agree there are many structs being introduced and that they're somewhat repetitive, but still not actually modular, and it's not a good read... Unfortunately this is how GitHub has structured the response schema. In this case I don't see that using struct embedding or reusable structs will make it any better. Possibly, I could introduce some:
```
type CopilotModelBase struct {
	Name                    string  `json:"name"`
	IsCustomModel           bool    `json:"is_custom_model"`
	CustomModelTrainingDate *string `json:"custom_model_training_date,omitempty"`
}
```
That would cut down the size of the `CopilotIDECodeCompletionsModel`, `CopilotIDEChatModel`, `CopilotDotcomChatModel`, and `CopilotDotcomPullRequestsModel` structs, but not by much and I'm not sure it makes them any nicer to work with.
gmlewis: @henriklundstrom - to proceed with this PR, it looks like you need to do the following steps:

* `git checkout master`
* `git pull upstream master` (or however you set up your repo to pull from the upstream repo)
* `git checkout nordnet:copilot-metrics`
* `git merge master`
* `./script/generate.sh` (see step 4 of CONTRIBUTING.md for more details)
* `git push origin nordnet:copilot-metrics`

This should pull #3352 into your PR which has the latest OpenAPI changes needed to support `copilot-metrics`.
henriklundstrom: @gmlewis I've updated the branch and we have a second approval now 🚀 
gmlewis: Thank you, @claystation !
Merging.

Patch:
diff --git a/github/copilot.go b/github/copilot.go
index 7ea13bd7..19cfd977 100644
--- a/github/copilot.go
+++ b/github/copilot.go
@@ -99,6 +99,125 @@ type CopilotUsageSummary struct {
 	Breakdown             []*CopilotUsageBreakdown `json:"breakdown"`
 }
 
+// CopilotMetricsListOptions represents the optional parameters to the CopilotService get metrics methods.
+type CopilotMetricsListOptions struct {
+	Since *time.Time `url:"since,omitempty"`
+	Until *time.Time `url:"until,omitempty"`
+
+	ListOptions
+}
+
+// CopilotIDECodeCompletionsLanguage represents Copilot usage metrics for completions in the IDE for a language.
+type CopilotIDECodeCompletionsLanguage struct {
+	Name              string `json:"name"`
+	TotalEngagedUsers int    `json:"total_engaged_users"`
+}
+
+// CopilotIDECodeCompletionsModelLanguage represents Copilot usage metrics for completions in the IDE for a model and language.
+type CopilotIDECodeCompletionsModelLanguage struct {
+	Name                    string `json:"name"`
+	TotalEngagedUsers       int    `json:"total_engaged_users"`
+	TotalCodeSuggestions    int    `json:"total_code_suggestions"`
+	TotalCodeAcceptances    int    `json:"total_code_acceptances"`
+	TotalCodeLinesSuggested int    `json:"total_code_lines_suggested"`
+	TotalCodeLinesAccepted  int    `json:"total_code_lines_accepted"`
+}
+
+// CopilotIDECodeCompletionsModel represents Copilot usage metrics for completions in the IDE for a model.
+type CopilotIDECodeCompletionsModel struct {
+	Name                    string                                    `json:"name"`
+	IsCustomModel           bool                                      `json:"is_custom_model"`
+	CustomModelTrainingDate *string                                   `json:"custom_model_training_date,omitempty"`
+	TotalEngagedUsers       int                                       `json:"total_engaged_users"`
+	Languages               []*CopilotIDECodeCompletionsModelLanguage `json:"languages"`
+}
+
+// CopilotIDECodeCompletionsEditor represents Copilot usage metrics for completions in the IDE for an editor.
+type CopilotIDECodeCompletionsEditor struct {
+	Name              string                            `json:"name"`
+	TotalEngagedUsers int                               `json:"total_engaged_users"`
+	Models            []*CopilotIDECodeCompletionsModel `json:"models"`
+}
+
+// CopilotIDECodeCompletions represents Copilot usage metrics for Copilot code completions in the IDE, categorized by editor, model and language.
+type CopilotIDECodeCompletions struct {
+	TotalEngagedUsers int                                  `json:"total_engaged_users"`
+	Languages         []*CopilotIDECodeCompletionsLanguage `json:"languages"`
+	Editors           []*CopilotIDECodeCompletionsEditor   `json:"editors"`
+}
+
+// CopilotIDEChatModel represents Copilot usage metrics for chatting with a model in the IDE.
+type CopilotIDEChatModel struct {
+	Name                     string  `json:"name"`
+	IsCustomModel            bool    `json:"is_custom_model"`
+	CustomModelTrainingDate  *string `json:"custom_model_training_date,omitempty"`
+	TotalEngagedUsers        int     `json:"total_engaged_users"`
+	TotalChats               int     `json:"total_chats"`
+	TotalChatInsertionEvents int     `json:"total_chat_insertion_events"`
+	TotalChatCopyEvents      int     `json:"total_chat_copy_events"`
+}
+
+// CopilotIDEChatEditor represents Copilot usage metrics for chatting with a model in the IDE, categorized by editor and model.
+type CopilotIDEChatEditor struct {
+	Name              string                 `json:"name"`
+	TotalEngagedUsers int                    `json:"total_engaged_users"`
+	Models            []*CopilotIDEChatModel `json:"models"`
+}
+
+// CopilotIDEChat represents Copilot usage metrics for Copilot Chat in the IDE, categorized by editor and model.
+type CopilotIDEChat struct {
+	TotalEngagedUsers int                     `json:"total_engaged_users"`
+	Editors           []*CopilotIDEChatEditor `json:"editors"`
+}
+
+// CopilotDotcomChatModel represents Copilot usage metrics for chatting with a model in the webbrowser.
+type CopilotDotcomChatModel struct {
+	Name                    string  `json:"name"`
+	IsCustomModel           bool    `json:"is_custom_model"`
+	CustomModelTrainingDate *string `json:"custom_model_training_date,omitempty"`
+	TotalEngagedUsers       int     `json:"total_engaged_users"`
+	TotalChats              int     `json:"total_chats"`
+}
+
+// CopilotDotcomChat represents Copilot usage metrics for Copilot Chat in the webbrowser, categorized by model.
+type CopilotDotcomChat struct {
+	TotalEngagedUsers int                       `json:"total_engaged_users"`
+	Models            []*CopilotDotcomChatModel `json:"models"`
+}
+
+// CopilotDotcomPullRequestsModel represents Copilot usage metrics for pull requests in the webbrowser, categorized by model.
+type CopilotDotcomPullRequestsModel struct {
+	Name                    string  `json:"name"`
+	IsCustomModel           bool    `json:"is_custom_model"`
+	CustomModelTrainingDate *string `json:"custom_model_training_date,omitempty"`
+	TotalPRSummariesCreated int     `json:"total_pr_summaries_created"`
+	TotalEngagedUsers       int     `json:"total_engaged_users"`
+}
+
+// CopilotDotcomPullRequestsRepository represents Copilot usage metrics for pull requests in the webbrowser, categorized by repository.
+type CopilotDotcomPullRequestsRepository struct {
+	Name              string                            `json:"name"`
+	TotalEngagedUsers int                               `json:"total_engaged_users"`
+	Models            []*CopilotDotcomPullRequestsModel `json:"models"`
+}
+
+// CopilotDotcomPullRequests represents Copilot usage metrics for pull requests in the webbrowser, categorized by repository and model.
+type CopilotDotcomPullRequests struct {
+	TotalEngagedUsers int                                    `json:"total_engaged_users"`
+	Repositories      []*CopilotDotcomPullRequestsRepository `json:"repositories"`
+}
+
+// CopilotMetrics represents Copilot usage metrics for a given day.
+type CopilotMetrics struct {
+	Date                      string                     `json:"date"`
+	TotalActiveUsers          *int                       `json:"total_active_users,omitempty"`
+	TotalEngagedUsers         *int                       `json:"total_engaged_users,omitempty"`
+	CopilotIDECodeCompletions *CopilotIDECodeCompletions `json:"copilot_ide_code_completions,omitempty"`
+	CopilotIDEChat            *CopilotIDEChat            `json:"copilot_ide_chat,omitempty"`
+	CopilotDotcomChat         *CopilotDotcomChat         `json:"copilot_dotcom_chat,omitempty"`
+	CopilotDotcomPullRequests *CopilotDotcomPullRequests `json:"copilot_dotcom_pull_requests,omitempty"`
+}
+
 func (cp *CopilotSeatDetails) UnmarshalJSON(data []byte) error {
 	// Using an alias to avoid infinite recursion when calling json.Unmarshal
 	type alias CopilotSeatDetails
@@ -482,3 +601,107 @@ func (s *CopilotService) GetOrganizationTeamUsage(ctx context.Context, org, team
 
 	return usage, resp, nil
 }
+
+// GetEnterpriseMetrics gets Copilot usage metrics for an enterprise.
+//
+// GitHub API docs: https://docs.github.com/rest/copilot/copilot-metrics#get-copilot-metrics-for-an-enterprise
+//
+//meta:operation GET /enterprises/{enterprise}/copilot/metrics
+func (s *CopilotService) GetEnterpriseMetrics(ctx context.Context, enterprise string, opts *CopilotMetricsListOptions) ([]*CopilotMetrics, *Response, error) {
+	u := fmt.Sprintf("enterprises/%v/copilot/metrics", 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
+	}
+
+	var metrics []*CopilotMetrics
+	resp, err := s.client.Do(ctx, req, &metrics)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return metrics, resp, nil
+}
+
+// GetEnterpriseTeamMetrics gets Copilot usage metrics for an enterprise team.
+//
+// GitHub API docs: https://docs.github.com/rest/copilot/copilot-metrics#get-copilot-metrics-for-an-enterprise-team
+//
+//meta:operation GET /enterprises/{enterprise}/team/{team_slug}/copilot/metrics
+func (s *CopilotService) GetEnterpriseTeamMetrics(ctx context.Context, enterprise, team string, opts *CopilotMetricsListOptions) ([]*CopilotMetrics, *Response, error) {
+	u := fmt.Sprintf("enterprises/%v/team/%v/copilot/metrics", enterprise, team)
+	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
+	}
+
+	var metrics []*CopilotMetrics
+	resp, err := s.client.Do(ctx, req, &metrics)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return metrics, resp, nil
+}
+
+// GetOrganizationMetrics gets Copilot usage metrics for an organization.
+//
+// GitHub API docs: https://docs.github.com/rest/copilot/copilot-metrics#get-copilot-metrics-for-an-organization
+//
+//meta:operation GET /orgs/{org}/copilot/metrics
+func (s *CopilotService) GetOrganizationMetrics(ctx context.Context, org string, opts *CopilotMetricsListOptions) ([]*CopilotMetrics, *Response, error) {
+	u := fmt.Sprintf("orgs/%v/copilot/metrics", org)
+	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
+	}
+
+	var metrics []*CopilotMetrics
+	resp, err := s.client.Do(ctx, req, &metrics)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return metrics, resp, nil
+}
+
+// GetOrganizationTeamMetrics gets Copilot usage metrics for an organization team.
+//
+// GitHub API docs: https://docs.github.com/rest/copilot/copilot-metrics#get-copilot-metrics-for-a-team
+//
+//meta:operation GET /orgs/{org}/team/{team_slug}/copilot/metrics
+func (s *CopilotService) GetOrganizationTeamMetrics(ctx context.Context, org, team string, opts *CopilotMetricsListOptions) ([]*CopilotMetrics, *Response, error) {
+	u := fmt.Sprintf("orgs/%v/team/%v/copilot/metrics", org, team)
+	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
+	}
+
+	var metrics []*CopilotMetrics
+	resp, err := s.client.Do(ctx, req, &metrics)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return metrics, resp, nil
+}
diff --git a/github/github-accessors.go b/github/github-accessors.go
index ed9353d1..0c7f150e 100644
--- a/github/github-accessors.go
+++ b/github/github-accessors.go
@@ -4294,6 +4294,102 @@ func (c *ContributorStats) GetTotal() int {
 	return *c.Total
 }
 
+// GetCustomModelTrainingDate returns the CustomModelTrainingDate field if it's non-nil, zero value otherwise.
+func (c *CopilotDotcomChatModel) GetCustomModelTrainingDate() string {
+	if c == nil || c.CustomModelTrainingDate == nil {
+		return ""
+	}
+	return *c.CustomModelTrainingDate
+}
+
+// GetCustomModelTrainingDate returns the CustomModelTrainingDate field if it's non-nil, zero value otherwise.
+func (c *CopilotDotcomPullRequestsModel) GetCustomModelTrainingDate() string {
+	if c == nil || c.CustomModelTrainingDate == nil {
+		return ""
+	}
+	return *c.CustomModelTrainingDate
+}
+
+// GetCustomModelTrainingDate returns the CustomModelTrainingDate field if it's non-nil, zero value otherwise.
+func (c *CopilotIDEChatModel) GetCustomModelTrainingDate() string {
+	if c == nil || c.CustomModelTrainingDate == nil {
+		return ""
+	}
+	return *c.CustomModelTrainingDate
+}
+
+// GetCustomModelTrainingDate returns the CustomModelTrainingDate field if it's non-nil, zero value otherwise.
+func (c *CopilotIDECodeCompletionsModel) GetCustomModelTrainingDate() string {
+	if c == nil || c.CustomModelTrainingDate == nil {
+		return ""
+	}
+	return *c.CustomModelTrainingDate
+}
+
+// GetCopilotDotcomChat returns the CopilotDotcomChat field.
+func (c *CopilotMetrics) GetCopilotDotcomChat() *CopilotDotcomChat {
+	if c == nil {
+		return nil
+	}
+	return c.CopilotDotcomChat
+}
+
+// GetCopilotDotcomPullRequests returns the CopilotDotcomPullRequests field.
+func (c *CopilotMetrics) GetCopilotDotcomPullRequests() *CopilotDotcomPullRequests {
+	if c == nil {
+		return nil
+	}
+	return c.CopilotDotcomPullRequests
+}
+
+// GetCopilotIDEChat returns the CopilotIDEChat field.
+func (c *CopilotMetrics) GetCopilotIDEChat() *CopilotIDEChat {
+	if c == nil {
+		return nil
+	}
+	return c.CopilotIDEChat
+}
+
+// GetCopilotIDECodeCompletions returns the CopilotIDECodeCompletions field.
+func (c *CopilotMetrics) GetCopilotIDECodeCompletions() *CopilotIDECodeCompletions {
+	if c == nil {
+		return nil
+	}
+	return c.CopilotIDECodeCompletions
+}
+
+// GetTotalActiveUsers returns the TotalActiveUsers field if it's non-nil, zero value otherwise.
+func (c *CopilotMetrics) GetTotalActiveUsers() int {
+	if c == nil || c.TotalActiveUsers == nil {
+		return 0
+	}
+	return *c.TotalActiveUsers
+}
+
+// GetTotalEngagedUsers returns the TotalEngagedUsers field if it's non-nil, zero value otherwise.
+func (c *CopilotMetrics) GetTotalEngagedUsers() int {
+	if c == nil || c.TotalEngagedUsers == nil {
+		return 0
+	}
+	return *c.TotalEngagedUsers
+}
+
+// GetSince returns the Since field if it's non-nil, zero value otherwise.
+func (c *CopilotMetricsListOptions) GetSince() time.Time {
+	if c == nil || c.Since == nil {
+		return time.Time{}
+	}
+	return *c.Since
+}
+
+// GetUntil returns the Until field if it's non-nil, zero value otherwise.
+func (c *CopilotMetricsListOptions) GetUntil() time.Time {
+	if c == nil || c.Until == nil {
+		return time.Time{}
+	}
+	return *c.Until
+}
+
 // GetSeatBreakdown returns the SeatBreakdown field.
 func (c *CopilotOrganizationDetails) GetSeatBreakdown() *CopilotSeatBreakdown {
 	if c == nil {


Test patch:
diff --git a/github/copilot_test.go b/github/copilot_test.go
index 6ac689a9..a299bf07 100644
--- a/github/copilot_test.go
+++ b/github/copilot_test.go
@@ -1930,3 +1930,1375 @@ func TestCopilotService_GetOrganizationTeamUsage(t *testing.T) {
 		return resp, err
 	})
 }
+
+func TestCopilotService_GetEnterpriseMetrics(t *testing.T) {
+	t.Parallel()
+	client, mux, _ := setup(t)
+
+	mux.HandleFunc("/enterprises/e/copilot/metrics", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `[
+			{
+				"date": "2024-06-24",
+				"total_active_users": 24,
+				"total_engaged_users": 20,
+				"copilot_ide_code_completions": {
+				"total_engaged_users": 20,
+				"languages": [
+					{
+					"name": "python",
+					"total_engaged_users": 10
+					},
+					{
+					"name": "ruby",
+					"total_engaged_users": 10
+					}
+				],
+				"editors": [
+					{
+					"name": "vscode",
+					"total_engaged_users": 13,
+					"models": [
+						{
+						"name": "default",
+						"is_custom_model": false,
+						"custom_model_training_date": null,
+						"total_engaged_users": 13,
+						"languages": [
+							{
+							"name": "python",
+							"total_engaged_users": 6,
+							"total_code_suggestions": 249,
+							"total_code_acceptances": 123,
+							"total_code_lines_suggested": 225,
+							"total_code_lines_accepted": 135
+							},
+							{
+							"name": "ruby",
+							"total_engaged_users": 7,
+							"total_code_suggestions": 496,
+							"total_code_acceptances": 253,
+							"total_code_lines_suggested": 520,
+							"total_code_lines_accepted": 270
+							}
+						]
+						}
+					]
+					},
+					{
+					"name": "neovim",
+					"total_engaged_users": 7,
+					"models": [
+						{
+						"name": "a-custom-model",
+						"is_custom_model": true,
+						"custom_model_training_date": "2024-02-01",
+						"languages": [
+							{
+							"name": "typescript",
+							"total_engaged_users": 3,
+							"total_code_suggestions": 112,
+							"total_code_acceptances": 56,
+							"total_code_lines_suggested": 143,
+							"total_code_lines_accepted": 61
+							},
+							{
+							"name": "go",
+							"total_engaged_users": 4,
+							"total_code_suggestions": 132,
+							"total_code_acceptances": 67,
+							"total_code_lines_suggested": 154,
+							"total_code_lines_accepted": 72
+							}
+						]
+						}
+					]
+					}
+				]
+				},
+				"copilot_ide_chat": {
+				"total_engaged_users": 13,
+				"editors": [
+					{
+					"name": "vscode",
+					"total_engaged_users": 13,
+					"models": [
+						{
+						"name": "default",
+						"is_custom_model": false,
+						"custom_model_training_date": null,
+						"total_engaged_users": 12,
+						"total_chats": 45,
+						"total_chat_insertion_events": 12,
+						"total_chat_copy_events": 16
+						},
+						{
+						"name": "a-custom-model",
+						"is_custom_model": true,
+						"custom_model_training_date": "2024-02-01",
+						"total_engaged_users": 1,
+						"total_chats": 10,
+						"total_chat_insertion_events": 11,
+						"total_chat_copy_events": 3
+						}
+					]
+					}
+				]
+				},
+				"copilot_dotcom_chat": {
+				"total_engaged_users": 14,
+				"models": [
+					{
+					"name": "default",
+					"is_custom_model": false,
+					"custom_model_training_date": null,
+					"total_engaged_users": 14,
+					"total_chats": 38
+					}
+				]
+				},
+				"copilot_dotcom_pull_requests": {
+				"total_engaged_users": 12,
+				"repositories": [
+					{
+					"name": "demo/repo1",
+					"total_engaged_users": 8,
+					"models": [
+						{
+						"name": "default",
+						"is_custom_model": false,
+						"custom_model_training_date": null,
+						"total_pr_summaries_created": 6,
+						"total_engaged_users": 8
+						}
+					]
+					},
+					{
+					"name": "demo/repo2",
+					"total_engaged_users": 4,
+					"models": [
+						{
+						"name": "a-custom-model",
+						"is_custom_model": true,
+						"custom_model_training_date": "2024-02-01",
+						"total_pr_summaries_created": 10,
+						"total_engaged_users": 4
+						}
+					]
+					}
+				]
+				}
+			}
+		]`)
+	})
+
+	ctx := context.Background()
+	got, _, err := client.Copilot.GetEnterpriseMetrics(ctx, "e", &CopilotMetricsListOptions{})
+	if err != nil {
+		t.Errorf("Copilot.GetEnterpriseMetrics returned error: %v", err)
+	}
+
+	totalActiveUsers := 24
+	totalEngagedUsers := 20
+	want := []*CopilotMetrics{
+		{
+			Date:              "2024-06-24",
+			TotalActiveUsers:  &totalActiveUsers,
+			TotalEngagedUsers: &totalEngagedUsers,
+			CopilotIDECodeCompletions: &CopilotIDECodeCompletions{
+				TotalEngagedUsers: 20,
+				Languages: []*CopilotIDECodeCompletionsLanguage{
+					{
+						Name:              "python",
+						TotalEngagedUsers: 10,
+					},
+					{
+						Name:              "ruby",
+						TotalEngagedUsers: 10,
+					},
+				},
+				Editors: []*CopilotIDECodeCompletionsEditor{
+					{
+						Name:              "vscode",
+						TotalEngagedUsers: 13,
+						Models: []*CopilotIDECodeCompletionsModel{
+							{
+								Name:                    "default",
+								IsCustomModel:           false,
+								CustomModelTrainingDate: nil,
+								TotalEngagedUsers:       13,
+								Languages: []*CopilotIDECodeCompletionsModelLanguage{
+									{
+										Name:                    "python",
+										TotalEngagedUsers:       6,
+										TotalCodeSuggestions:    249,
+										TotalCodeAcceptances:    123,
+										TotalCodeLinesSuggested: 225,
+										TotalCodeLinesAccepted:  135,
+									},
+									{
+										Name:                    "ruby",
+										TotalEngagedUsers:       7,
+										TotalCodeSuggestions:    496,
+										TotalCodeAcceptances:    253,
+										TotalCodeLinesSuggested: 520,
+										TotalCodeLinesAccepted:  270,
+									},
+								},
+							},
+						},
+					},
+					{
+						Name:              "neovim",
+						TotalEngagedUsers: 7,
+						Models: []*CopilotIDECodeCompletionsModel{
+							{
+								Name:                    "a-custom-model",
+								IsCustomModel:           true,
+								CustomModelTrainingDate: String("2024-02-01"),
+								Languages: []*CopilotIDECodeCompletionsModelLanguage{
+									{
+										Name:                    "typescript",
+										TotalEngagedUsers:       3,
+										TotalCodeSuggestions:    112,
+										TotalCodeAcceptances:    56,
+										TotalCodeLinesSuggested: 143,
+										TotalCodeLinesAccepted:  61,
+									},
+									{
+										Name:                    "go",
+										TotalEngagedUsers:       4,
+										TotalCodeSuggestions:    132,
+										TotalCodeAcceptances:    67,
+										TotalCodeLinesSuggested: 154,
+										TotalCodeLinesAccepted:  72,
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			CopilotIDEChat: &CopilotIDEChat{
+				TotalEngagedUsers: 13,
+				Editors: []*CopilotIDEChatEditor{
+					{
+						Name:              "vscode",
+						TotalEngagedUsers: 13,
+						Models: []*CopilotIDEChatModel{
+							{
+								Name:                     "default",
+								IsCustomModel:            false,
+								CustomModelTrainingDate:  nil,
+								TotalEngagedUsers:        12,
+								TotalChats:               45,
+								TotalChatInsertionEvents: 12,
+								TotalChatCopyEvents:      16,
+							},
+							{
+								Name:                     "a-custom-model",
+								IsCustomModel:            true,
+								CustomModelTrainingDate:  String("2024-02-01"),
+								TotalEngagedUsers:        1,
+								TotalChats:               10,
+								TotalChatInsertionEvents: 11,
+								TotalChatCopyEvents:      3,
+							},
+						},
+					},
+				},
+			},
+			CopilotDotcomChat: &CopilotDotcomChat{
+				TotalEngagedUsers: 14,
+				Models: []*CopilotDotcomChatModel{
+					{
+						Name:                    "default",
+						IsCustomModel:           false,
+						CustomModelTrainingDate: nil,
+						TotalEngagedUsers:       14,
+						TotalChats:              38,
+					},
+				},
+			},
+			CopilotDotcomPullRequests: &CopilotDotcomPullRequests{
+				TotalEngagedUsers: 12,
+				Repositories: []*CopilotDotcomPullRequestsRepository{
+					{
+						Name:              "demo/repo1",
+						TotalEngagedUsers: 8,
+						Models: []*CopilotDotcomPullRequestsModel{
+							{
+								Name:                    "default",
+								IsCustomModel:           false,
+								CustomModelTrainingDate: nil,
+								TotalPRSummariesCreated: 6,
+								TotalEngagedUsers:       8,
+							},
+						},
+					},
+					{
+						Name:              "demo/repo2",
+						TotalEngagedUsers: 4,
+						Models: []*CopilotDotcomPullRequestsModel{
+							{
+								Name:                    "a-custom-model",
+								IsCustomModel:           true,
+								CustomModelTrainingDate: String("2024-02-01"),
+								TotalPRSummariesCreated: 10,
+								TotalEngagedUsers:       4,
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	if !cmp.Equal(got, want) {
+		t.Errorf("Copilot.GetEnterpriseMetrics returned %+v, want %+v", got, want)
+	}
+
+	const methodName = "GetEnterpriseMetrics"
+
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Copilot.GetEnterpriseMetrics(ctx, "\n", &CopilotMetricsListOptions{})
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Copilot.GetEnterpriseMetrics(ctx, "e", &CopilotMetricsListOptions{})
+		if got != nil {
+			t.Errorf("Copilot.GetEnterpriseMetrics returned %+v, want nil", got)
+		}
+		return resp, err
+	})
+}
+
+func TestCopilotService_GetEnterpriseTeamMetrics(t *testing.T) {
+	t.Parallel()
+	client, mux, _ := setup(t)
+
+	mux.HandleFunc("/enterprises/e/team/t/copilot/metrics", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `[
+			{
+				"date": "2024-06-24",
+				"total_active_users": 24,
+				"total_engaged_users": 20,
+				"copilot_ide_code_completions": {
+				"total_engaged_users": 20,
+				"languages": [
+					{
+					"name": "python",
+					"total_engaged_users": 10
+					},
+					{
+					"name": "ruby",
+					"total_engaged_users": 10
+					}
+				],
+				"editors": [
+					{
+					"name": "vscode",
+					"total_engaged_users": 13,
+					"models": [
+						{
+						"name": "default",
+						"is_custom_model": false,
+						"custom_model_training_date": null,
+						"total_engaged_users": 13,
+						"languages": [
+							{
+							"name": "python",
+							"total_engaged_users": 6,
+							"total_code_suggestions": 249,
+							"total_code_acceptances": 123,
+							"total_code_lines_suggested": 225,
+							"total_code_lines_accepted": 135
+							},
+							{
+							"name": "ruby",
+							"total_engaged_users": 7,
+							"total_code_suggestions": 496,
+							"total_code_acceptances": 253,
+							"total_code_lines_suggested": 520,
+							"total_code_lines_accepted": 270
+							}
+						]
+						}
+					]
+					},
+					{
+					"name": "neovim",
+					"total_engaged_users": 7,
+					"models": [
+						{
+						"name": "a-custom-model",
+						"is_custom_model": true,
+						"custom_model_training_date": "2024-02-01",
+						"languages": [
+							{
+							"name": "typescript",
+							"total_engaged_users": 3,
+							"total_code_suggestions": 112,
+							"total_code_acceptances": 56,
+							"total_code_lines_suggested": 143,
+							"total_code_lines_accepted": 61
+							},
+							{
+							"name": "go",
+							"total_engaged_users": 4,
+							"total_code_suggestions": 132,
+							"total_code_acceptances": 67,
+							"total_code_lines_suggested": 154,
+							"total_code_lines_accepted": 72
+							}
+						]
+						}
+					]
+					}
+				]
+				},
+				"copilot_ide_chat": {
+				"total_engaged_users": 13,
+				"editors": [
+					{
+					"name": "vscode",
+					"total_engaged_users": 13,
+					"models": [
+						{
+						"name": "default",
+						"is_custom_model": false,
+						"custom_model_training_date": null,
+						"total_engaged_users": 12,
+						"total_chats": 45,
+						"total_chat_insertion_events": 12,
+						"total_chat_copy_events": 16
+						},
+						{
+						"name": "a-custom-model",
+						"is_custom_model": true,
+						"custom_model_training_date": "2024-02-01",
+						"total_engaged_users": 1,
+						"total_chats": 10,
+						"total_chat_insertion_events": 11,
+						"total_chat_copy_events": 3
+						}
+					]
+					}
+				]
+				},
+				"copilot_dotcom_chat": {
+				"total_engaged_users": 14,
+				"models": [
+					{
+					"name": "default",
+					"is_custom_model": false,
+					"custom_model_training_date": null,
+					"total_engaged_users": 14,
+					"total_chats": 38
+					}
+				]
+				},
+				"copilot_dotcom_pull_requests": {
+				"total_engaged_users": 12,
+				"repositories": [
+					{
+					"name": "demo/repo1",
+					"total_engaged_users": 8,
+					"models": [
+						{
+						"name": "default",
+						"is_custom_model": false,
+						"custom_model_training_date": null,
+						"total_pr_summaries_created": 6,
+						"total_engaged_users": 8
+						}
+					]
+					},
+					{
+					"name": "demo/repo2",
+					"total_engaged_users": 4,
+					"models": [
+						{
+						"name": "a-custom-model",
+						"is_custom_model": true,
+						"custom_model_training_date": "2024-02-01",
+						"total_pr_summaries_created": 10,
+						"total_engaged_users": 4
+						}
+					]
+					}
+				]
+				}
+			}
+		]`)
+	})
+
+	ctx := context.Background()
+	got, _, err := client.Copilot.GetEnterpriseTeamMetrics(ctx, "e", "t", &CopilotMetricsListOptions{})
+	if err != nil {
+		t.Errorf("Copilot.GetEnterpriseTeamMetrics returned error: %v", err)
+	}
+
+	totalActiveUsers := 24
+	totalEngagedUsers := 20
+	want := []*CopilotMetrics{
+		{
+			Date:              "2024-06-24",
+			TotalActiveUsers:  &totalActiveUsers,
+			TotalEngagedUsers: &totalEngagedUsers,
+			CopilotIDECodeCompletions: &CopilotIDECodeCompletions{
+				TotalEngagedUsers: 20,
+				Languages: []*CopilotIDECodeCompletionsLanguage{
+					{
+						Name:              "python",
+						TotalEngagedUsers: 10,
+					},
+					{
+						Name:              "ruby",
+						TotalEngagedUsers: 10,
+					},
+				},
+				Editors: []*CopilotIDECodeCompletionsEditor{
+					{
+						Name:              "vscode",
+						TotalEngagedUsers: 13,
+						Models: []*CopilotIDECodeCompletionsModel{
+							{
+								Name:                    "default",
+								IsCustomModel:           false,
+								CustomModelTrainingDate: nil,
+								TotalEngagedUsers:       13,
+								Languages: []*CopilotIDECodeCompletionsModelLanguage{
+									{
+										Name:                    "python",
+										TotalEngagedUsers:       6,
+										TotalCodeSuggestions:    249,
+										TotalCodeAcceptances:    123,
+										TotalCodeLinesSuggested: 225,
+										TotalCodeLinesAccepted:  135,
+									},
+									{
+										Name:                    "ruby",
+										TotalEngagedUsers:       7,
+										TotalCodeSuggestions:    496,
+										TotalCodeAcceptances:    253,
+										TotalCodeLinesSuggested: 520,
+										TotalCodeLinesAccepted:  270,
+									},
+								},
+							},
+						},
+					},
+					{
+						Name:              "neovim",
+						TotalEngagedUsers: 7,
+						Models: []*CopilotIDECodeCompletionsModel{
+							{
+								Name:                    "a-custom-model",
+								IsCustomModel:           true,
+								CustomModelTrainingDate: String("2024-02-01"),
+								Languages: []*CopilotIDECodeCompletionsModelLanguage{
+									{
+										Name:                    "typescript",
+										TotalEngagedUsers:       3,
+										TotalCodeSuggestions:    112,
+										TotalCodeAcceptances:    56,
+										TotalCodeLinesSuggested: 143,
+										TotalCodeLinesAccepted:  61,
+									},
+									{
+										Name:                    "go",
+										TotalEngagedUsers:       4,
+										TotalCodeSuggestions:    132,
+										TotalCodeAcceptances:    67,
+										TotalCodeLinesSuggested: 154,
+										TotalCodeLinesAccepted:  72,
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			CopilotIDEChat: &CopilotIDEChat{
+				TotalEngagedUsers: 13,
+				Editors: []*CopilotIDEChatEditor{
+					{
+						Name:              "vscode",
+						TotalEngagedUsers: 13,
+						Models: []*CopilotIDEChatModel{
+							{
+								Name:                     "default",
+								IsCustomModel:            false,
+								CustomModelTrainingDate:  nil,
+								TotalEngagedUsers:        12,
+								TotalChats:               45,
+								TotalChatInsertionEvents: 12,
+								TotalChatCopyEvents:      16,
+							},
+							{
+								Name:                     "a-custom-model",
+								IsCustomModel:            true,
+								CustomModelTrainingDate:  String("2024-02-01"),
+								TotalEngagedUsers:        1,
+								TotalChats:               10,
+								TotalChatInsertionEvents: 11,
+								TotalChatCopyEvents:      3,
+							},
+						},
+					},
+				},
+			},
+			CopilotDotcomChat: &CopilotDotcomChat{
+				TotalEngagedUsers: 14,
+				Models: []*CopilotDotcomChatModel{
+					{
+						Name:                    "default",
+						IsCustomModel:           false,
+						CustomModelTrainingDate: nil,
+						TotalEngagedUsers:       14,
+						TotalChats:              38,
+					},
+				},
+			},
+			CopilotDotcomPullRequests: &CopilotDotcomPullRequests{
+				TotalEngagedUsers: 12,
+				Repositories: []*CopilotDotcomPullRequestsRepository{
+					{
+						Name:              "demo/repo1",
+						TotalEngagedUsers: 8,
+						Models: []*CopilotDotcomPullRequestsModel{
+							{
+								Name:                    "default",
+								IsCustomModel:           false,
+								CustomModelTrainingDate: nil,
+								TotalPRSummariesCreated: 6,
+								TotalEngagedUsers:       8,
+							},
+						},
+					},
+					{
+						Name:              "demo/repo2",
+						TotalEngagedUsers: 4,
+						Models: []*CopilotDotcomPullRequestsModel{
+							{
+								Name:                    "a-custom-model",
+								IsCustomModel:           true,
+								CustomModelTrainingDate: String("2024-02-01"),
+								TotalPRSummariesCreated: 10,
+								TotalEngagedUsers:       4,
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	if !cmp.Equal(got, want) {
+		t.Errorf("Copilot.GetEnterpriseTeamMetrics returned %+v, want %+v", got, want)
+	}
+
+	const methodName = "GetEnterpriseTeamMetrics"
+
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Copilot.GetEnterpriseTeamMetrics(ctx, "\n", "t", &CopilotMetricsListOptions{})
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Copilot.GetEnterpriseTeamMetrics(ctx, "e", "t", &CopilotMetricsListOptions{})
+		if got != nil {
+			t.Errorf("Copilot.GetEnterpriseTeamMetrics returned %+v, want nil", got)
+		}
+		return resp, err
+	})
+}
+
+func TestCopilotService_GetOrganizationMetrics(t *testing.T) {
+	t.Parallel()
+	client, mux, _ := setup(t)
+
+	mux.HandleFunc("/orgs/o/copilot/metrics", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `[
+			{
+				"date": "2024-06-24",
+				"total_active_users": 24,
+				"total_engaged_users": 20,
+				"copilot_ide_code_completions": {
+				"total_engaged_users": 20,
+				"languages": [
+					{
+					"name": "python",
+					"total_engaged_users": 10
+					},
+					{
+					"name": "ruby",
+					"total_engaged_users": 10
+					}
+				],
+				"editors": [
+					{
+					"name": "vscode",
+					"total_engaged_users": 13,
+					"models": [
+						{
+						"name": "default",
+						"is_custom_model": false,
+						"custom_model_training_date": null,
+						"total_engaged_users": 13,
+						"languages": [
+							{
+							"name": "python",
+							"total_engaged_users": 6,
+							"total_code_suggestions": 249,
+							"total_code_acceptances": 123,
+							"total_code_lines_suggested": 225,
+							"total_code_lines_accepted": 135
+							},
+							{
+							"name": "ruby",
+							"total_engaged_users": 7,
+							"total_code_suggestions": 496,
+							"total_code_acceptances": 253,
+							"total_code_lines_suggested": 520,
+							"total_code_lines_accepted": 270
+							}
+						]
+						}
+					]
+					},
+					{
+					"name": "neovim",
+					"total_engaged_users": 7,
+					"models": [
+						{
+						"name": "a-custom-model",
+						"is_custom_model": true,
+						"custom_model_training_date": "2024-02-01",
+						"languages": [
+							{
+							"name": "typescript",
+							"total_engaged_users": 3,
+							"total_code_suggestions": 112,
+							"total_code_acceptances": 56,
+							"total_code_lines_suggested": 143,
+							"total_code_lines_accepted": 61
+							},
+							{
+							"name": "go",
+							"total_engaged_users": 4,
+							"total_code_suggestions": 132,
+							"total_code_acceptances": 67,
+							"total_code_lines_suggested": 154,
+							"total_code_lines_accepted": 72
+							}
+						]
+						}
+					]
+					}
+				]
+				},
+				"copilot_ide_chat": {
+				"total_engaged_users": 13,
+				"editors": [
+					{
+					"name": "vscode",
+					"total_engaged_users": 13,
+					"models": [
+						{
+						"name": "default",
+						"is_custom_model": false,
+						"custom_model_training_date": null,
+						"total_engaged_users": 12,
+						"total_chats": 45,
+						"total_chat_insertion_events": 12,
+						"total_chat_copy_events": 16
+						},
+						{
+						"name": "a-custom-model",
+						"is_custom_model": true,
+						"custom_model_training_date": "2024-02-01",
+						"total_engaged_users": 1,
+						"total_chats": 10,
+						"total_chat_insertion_events": 11,
+						"total_chat_copy_events": 3
+						}
+					]
+					}
+				]
+				},
+				"copilot_dotcom_chat": {
+				"total_engaged_users": 14,
+				"models": [
+					{
+					"name": "default",
+					"is_custom_model": false,
+					"custom_model_training_date": null,
+					"total_engaged_users": 14,
+					"total_chats": 38
+					}
+				]
+				},
+				"copilot_dotcom_pull_requests": {
+				"total_engaged_users": 12,
+				"repositories": [
+					{
+					"name": "demo/repo1",
+					"total_engaged_users": 8,
+					"models": [
+						{
+						"name": "default",
+						"is_custom_model": false,
+						"custom_model_training_date": null,
+						"total_pr_summaries_created": 6,
+						"total_engaged_users": 8
+						}
+					]
+					},
+					{
+					"name": "demo/repo2",
+					"total_engaged_users": 4,
+					"models": [
+						{
+						"name": "a-custom-model",
+						"is_custom_model": true,
+						"custom_model_training_date": "2024-02-01",
+						"total_pr_summaries_created": 10,
+						"total_engaged_users": 4
+						}
+					]
+					}
+				]
+				}
+			}
+		]`)
+	})
+
+	ctx := context.Background()
+	got, _, err := client.Copilot.GetOrganizationMetrics(ctx, "o", &CopilotMetricsListOptions{})
+	if err != nil {
+		t.Errorf("Copilot.GetOrganizationMetrics returned error: %v", err)
+	}
+
+	totalActiveUsers := 24
+	totalEngagedUsers := 20
+	want := []*CopilotMetrics{
+		{
+			Date:              "2024-06-24",
+			TotalActiveUsers:  &totalActiveUsers,
+			TotalEngagedUsers: &totalEngagedUsers,
+			CopilotIDECodeCompletions: &CopilotIDECodeCompletions{
+				TotalEngagedUsers: 20,
+				Languages: []*CopilotIDECodeCompletionsLanguage{
+					{
+						Name:              "python",
+						TotalEngagedUsers: 10,
+					},
+					{
+						Name:              "ruby",
+						TotalEngagedUsers: 10,
+					},
+				},
+				Editors: []*CopilotIDECodeCompletionsEditor{
+					{
+						Name:              "vscode",
+						TotalEngagedUsers: 13,
+						Models: []*CopilotIDECodeCompletionsModel{
+							{
+								Name:                    "default",
+								IsCustomModel:           false,
+								CustomModelTrainingDate: nil,
+								TotalEngagedUsers:       13,
+								Languages: []*CopilotIDECodeCompletionsModelLanguage{
+									{
+										Name:                    "python",
+										TotalEngagedUsers:       6,
+										TotalCodeSuggestions:    249,
+										TotalCodeAcceptances:    123,
+										TotalCodeLinesSuggested: 225,
+										TotalCodeLinesAccepted:  135,
+									},
+									{
+										Name:                    "ruby",
+										TotalEngagedUsers:       7,
+										TotalCodeSuggestions:    496,
+										TotalCodeAcceptances:    253,
+										TotalCodeLinesSuggested: 520,
+										TotalCodeLinesAccepted:  270,
+									},
+								},
+							},
+						},
+					},
+					{
+						Name:              "neovim",
+						TotalEngagedUsers: 7,
+						Models: []*CopilotIDECodeCompletionsModel{
+							{
+								Name:                    "a-custom-model",
+								IsCustomModel:           true,
+								CustomModelTrainingDate: String("2024-02-01"),
+								Languages: []*CopilotIDECodeCompletionsModelLanguage{
+									{
+										Name:                    "typescript",
+										TotalEngagedUsers:       3,
+										TotalCodeSuggestions:    112,
+										TotalCodeAcceptances:    56,
+										TotalCodeLinesSuggested: 143,
+										TotalCodeLinesAccepted:  61,
+									},
+									{
+										Name:                    "go",
+										TotalEngagedUsers:       4,
+										TotalCodeSuggestions:    132,
+										TotalCodeAcceptances:    67,
+										TotalCodeLinesSuggested: 154,
+										TotalCodeLinesAccepted:  72,
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			CopilotIDEChat: &CopilotIDEChat{
+				TotalEngagedUsers: 13,
+				Editors: []*CopilotIDEChatEditor{
+					{
+						Name:              "vscode",
+						TotalEngagedUsers: 13,
+						Models: []*CopilotIDEChatModel{
+							{
+								Name:                     "default",
+								IsCustomModel:            false,
+								CustomModelTrainingDate:  nil,
+								TotalEngagedUsers:        12,
+								TotalChats:               45,
+								TotalChatInsertionEvents: 12,
+								TotalChatCopyEvents:      16,
+							},
+							{
+								Name:                     "a-custom-model",
+								IsCustomModel:            true,
+								CustomModelTrainingDate:  String("2024-02-01"),
+								TotalEngagedUsers:        1,
+								TotalChats:               10,
+								TotalChatInsertionEvents: 11,
+								TotalChatCopyEvents:      3,
+							},
+						},
+					},
+				},
+			},
+			CopilotDotcomChat: &CopilotDotcomChat{
+				TotalEngagedUsers: 14,
+				Models: []*CopilotDotcomChatModel{
+					{
+						Name:                    "default",
+						IsCustomModel:           false,
+						CustomModelTrainingDate: nil,
+						TotalEngagedUsers:       14,
+						TotalChats:              38,
+					},
+				},
+			},
+			CopilotDotcomPullRequests: &CopilotDotcomPullRequests{
+				TotalEngagedUsers: 12,
+				Repositories: []*CopilotDotcomPullRequestsRepository{
+					{
+						Name:              "demo/repo1",
+						TotalEngagedUsers: 8,
+						Models: []*CopilotDotcomPullRequestsModel{
+							{
+								Name:                    "default",
+								IsCustomModel:           false,
+								CustomModelTrainingDate: nil,
+								TotalPRSummariesCreated: 6,
+								TotalEngagedUsers:       8,
+							},
+						},
+					},
+					{
+						Name:              "demo/repo2",
+						TotalEngagedUsers: 4,
+						Models: []*CopilotDotcomPullRequestsModel{
+							{
+								Name:                    "a-custom-model",
+								IsCustomModel:           true,
+								CustomModelTrainingDate: String("2024-02-01"),
+								TotalPRSummariesCreated: 10,
+								TotalEngagedUsers:       4,
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	if !cmp.Equal(got, want) {
+		t.Errorf("Copilot.GetOrganizationMetrics returned %+v, want %+v", got, want)
+	}
+
+	const methodName = "GetOrganizationMetrics"
+
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Copilot.GetOrganizationMetrics(ctx, "\n", &CopilotMetricsListOptions{})
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Copilot.GetOrganizationMetrics(ctx, "o", &CopilotMetricsListOptions{})
+		if got != nil {
+			t.Errorf("Copilot.GetOrganizationMetrics returned %+v, want nil", got)
+		}
+		return resp, err
+	})
+}
+
+func TestCopilotService_GetOrganizationTeamMetrics(t *testing.T) {
+	t.Parallel()
+	client, mux, _ := setup(t)
+
+	mux.HandleFunc("/orgs/o/team/t/copilot/metrics", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `[
+			{
+				"date": "2024-06-24",
+				"total_active_users": 24,
+				"total_engaged_users": 20,
+				"copilot_ide_code_completions": {
+					"total_engaged_users": 20,
+					"languages": [
+						{
+							"name": "python",
+							"total_engaged_users": 10
+						},
+						{
+							"name": "ruby",
+							"total_engaged_users": 10
+						}
+					],
+					"editors": [
+						{
+							"name": "vscode",
+							"total_engaged_users": 13,
+							"models": [
+								{
+									"name": "default",
+									"is_custom_model": false,
+									"custom_model_training_date": null,
+									"total_engaged_users": 13,
+									"languages": [
+										{
+											"name": "python",
+											"total_engaged_users": 6,
+											"total_code_suggestions": 249,
+											"total_code_acceptances": 123,
+											"total_code_lines_suggested": 225,
+											"total_code_lines_accepted": 135
+										},
+										{
+											"name": "ruby",
+											"total_engaged_users": 7,
+											"total_code_suggestions": 496,
+											"total_code_acceptances": 253,
+											"total_code_lines_suggested": 520,
+											"total_code_lines_accepted": 270
+										}
+									]
+								}
+							]
+						},
+						{
+							"name": "neovim",
+							"total_engaged_users": 7,
+							"models": [
+								{
+									"name": "a-custom-model",
+									"is_custom_model": true,
+									"custom_model_training_date": "2024-02-01",
+									"languages": [
+										{
+											"name": "typescript",
+											"total_engaged_users": 3,
+											"total_code_suggestions": 112,
+											"total_code_acceptances": 56,
+											"total_code_lines_suggested": 143,
+											"total_code_lines_accepted": 61
+										},
+										{
+											"name": "go",
+											"total_engaged_users": 4,
+											"total_code_suggestions": 132,
+											"total_code_acceptances": 67,
+											"total_code_lines_suggested": 154,
+											"total_code_lines_accepted": 72
+										}
+									]
+								}
+							]
+						}
+					]
+				},
+				"copilot_ide_chat": {
+					"total_engaged_users": 13,
+					"editors": [
+						{
+							"name": "vscode",
+							"total_engaged_users": 13,
+							"models": [
+								{
+									"name": "default",
+									"is_custom_model": false,
+									"custom_model_training_date": null,
+									"total_engaged_users": 12,
+									"total_chats": 45,
+									"total_chat_insertion_events": 12,
+									"total_chat_copy_events": 16
+								},
+								{
+									"name": "a-custom-model",
+									"is_custom_model": true,
+									"custom_model_training_date": "2024-02-01",
+									"total_engaged_users": 1,
+									"total_chats": 10,
+									"total_chat_insertion_events": 11,
+									"total_chat_copy_events": 3
+								}
+							]
+						}
+					]
+				},
+				"copilot_dotcom_chat": {
+					"total_engaged_users": 14,
+					"models": [
+						{
+							"name": "default",
+							"is_custom_model": false,
+							"custom_model_training_date": null,
+							"total_engaged_users": 14,
+							"total_chats": 38
+						}
+					]
+				},
+				"copilot_dotcom_pull_requests": {
+					"total_engaged_users": 12,
+					"repositories": [
+						{
+							"name": "demo/repo1",
+							"total_engaged_users": 8,
+							"models": [
+								{
+									"name": "default",
+									"is_custom_model": false,
+									"custom_model_training_date": null,
+									"total_pr_summaries_created": 6,
+									"total_engaged_users": 8
+								}
+							]
+						},
+						{
+							"name": "demo/repo2",
+							"total_engaged_users": 4,
+							"models": [
+								{
+									"name": "a-custom-model",
+									"is_custom_model": true,
+									"custom_model_training_date": "2024-02-01",
+									"total_pr_summaries_created": 10,
+									"total_engaged_users": 4
+								}
+							]
+						}
+					]
+				}
+			}
+		]`)
+	})
+
+	ctx := context.Background()
+	got, _, err := client.Copilot.GetOrganizationTeamMetrics(ctx, "o", "t", &CopilotMetricsListOptions{})
+	if err != nil {
+		t.Errorf("Copilot.GetOrganizationTeamMetrics returned error: %v", err)
+	}
+
+	totalActiveUsers := 24
+	totalEngagedUsers := 20
+	want := []*CopilotMetrics{
+		{
+			Date:              "2024-06-24",
+			TotalActiveUsers:  &totalActiveUsers,
+			TotalEngagedUsers: &totalEngagedUsers,
+			CopilotIDECodeCompletions: &CopilotIDECodeCompletions{
+				TotalEngagedUsers: 20,
+				Languages: []*CopilotIDECodeCompletionsLanguage{
+					{
+						Name:              "python",
+						TotalEngagedUsers: 10,
+					},
+					{
+						Name:              "ruby",
+						TotalEngagedUsers: 10,
+					},
+				},
+				Editors: []*CopilotIDECodeCompletionsEditor{
+					{
+						Name:              "vscode",
+						TotalEngagedUsers: 13,
+						Models: []*CopilotIDECodeCompletionsModel{
+							{
+								Name:                    "default",
+								IsCustomModel:           false,
+								CustomModelTrainingDate: nil,
+								TotalEngagedUsers:       13,
+								Languages: []*CopilotIDECodeCompletionsModelLanguage{
+									{
+										Name:                    "python",
+										TotalEngagedUsers:       6,
+										TotalCodeSuggestions:    249,
+										TotalCodeAcceptances:    123,
+										TotalCodeLinesSuggested: 225,
+										TotalCodeLinesAccepted:  135,
+									},
+									{
+										Name:                    "ruby",
+										TotalEngagedUsers:       7,
+										TotalCodeSuggestions:    496,
+										TotalCodeAcceptances:    253,
+										TotalCodeLinesSuggested: 520,
+										TotalCodeLinesAccepted:  270,
+									},
+								},
+							},
+						},
+					},
+					{
+						Name:              "neovim",
+						TotalEngagedUsers: 7,
+						Models: []*CopilotIDECodeCompletionsModel{
+							{
+								Name:                    "a-custom-model",
+								IsCustomModel:           true,
+								CustomModelTrainingDate: String("2024-02-01"),
+								Languages: []*CopilotIDECodeCompletionsModelLanguage{
+									{
+										Name:                    "typescript",
+										TotalEngagedUsers:       3,
+										TotalCodeSuggestions:    112,
+										TotalCodeAcceptances:    56,
+										TotalCodeLinesSuggested: 143,
+										TotalCodeLinesAccepted:  61,
+									},
+									{
+										Name:                    "go",
+										TotalEngagedUsers:       4,
+										TotalCodeSuggestions:    132,
+										TotalCodeAcceptances:    67,
+										TotalCodeLinesSuggested: 154,
+										TotalCodeLinesAccepted:  72,
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			CopilotIDEChat: &CopilotIDEChat{
+				TotalEngagedUsers: 13,
+				Editors: []*CopilotIDEChatEditor{
+					{
+						Name:              "vscode",
+						TotalEngagedUsers: 13,
+						Models: []*CopilotIDEChatModel{
+							{
+								Name:                     "default",
+								IsCustomModel:            false,
+								CustomModelTrainingDate:  nil,
+								TotalEngagedUsers:        12,
+								TotalChats:               45,
+								TotalChatInsertionEvents: 12,
+								TotalChatCopyEvents:      16,
+							},
+							{
+								Name:                     "a-custom-model",
+								IsCustomModel:            true,
+								CustomModelTrainingDate:  String("2024-02-01"),
+								TotalEngagedUsers:        1,
+								TotalChats:               10,
+								TotalChatInsertionEvents: 11,
+								TotalChatCopyEvents:      3,
+							},
+						},
+					},
+				},
+			},
+			CopilotDotcomChat: &CopilotDotcomChat{
+				TotalEngagedUsers: 14,
+				Models: []*CopilotDotcomChatModel{
+					{
+						Name:                    "default",
+						IsCustomModel:           false,
+						CustomModelTrainingDate: nil,
+						TotalEngagedUsers:       14,
+						TotalChats:              38,
+					},
+				},
+			},
+			CopilotDotcomPullRequests: &CopilotDotcomPullRequests{
+				TotalEngagedUsers: 12,
+				Repositories: []*CopilotDotcomPullRequestsRepository{
+					{
+						Name:              "demo/repo1",
+						TotalEngagedUsers: 8,
+						Models: []*CopilotDotcomPullRequestsModel{
+							{
+								Name:                    "default",
+								IsCustomModel:           false,
+								CustomModelTrainingDate: nil,
+								TotalPRSummariesCreated: 6,
+								TotalEngagedUsers:       8,
+							},
+						},
+					},
+					{
+						Name:              "demo/repo2",
+						TotalEngagedUsers: 4,
+						Models: []*CopilotDotcomPullRequestsModel{
+							{
+								Name:                    "a-custom-model",
+								IsCustomModel:           true,
+								CustomModelTrainingDate: String("2024-02-01"),
+								TotalPRSummariesCreated: 10,
+								TotalEngagedUsers:       4,
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	if !cmp.Equal(got, want) {
+		t.Errorf("Copilot.GetOrganizationTeamMetrics returned %+v, want %+v", got, want)
+	}
+
+	const methodName = "GetOrganizationTeamMetrics"
+
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Copilot.GetOrganizationTeamMetrics(ctx, "\n", "\n", &CopilotMetricsListOptions{})
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Copilot.GetOrganizationTeamMetrics(ctx, "o", "t", &CopilotMetricsListOptions{})
+		if got != nil {
+			t.Errorf("Copilot.GetOrganizationTeamMetrics returned %+v, want nil", got)
+		}
+		return resp, err
+	})
+}
diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go
index 4269618a..28d75f3e 100644
--- a/github/github-accessors_test.go
+++ b/github/github-accessors_test.go
@@ -5564,6 +5564,126 @@ func TestContributorStats_GetTotal(tt *testing.T) {
 	c.GetTotal()
 }
 
+func TestCopilotDotcomChatModel_GetCustomModelTrainingDate(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	c := &CopilotDotcomChatModel{CustomModelTrainingDate: &zeroValue}
+	c.GetCustomModelTrainingDate()
+	c = &CopilotDotcomChatModel{}
+	c.GetCustomModelTrainingDate()
+	c = nil
+	c.GetCustomModelTrainingDate()
+}
+
+func TestCopilotDotcomPullRequestsModel_GetCustomModelTrainingDate(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	c := &CopilotDotcomPullRequestsModel{CustomModelTrainingDate: &zeroValue}
+	c.GetCustomModelTrainingDate()
+	c = &CopilotDotcomPullRequestsModel{}
+	c.GetCustomModelTrainingDate()
+	c = nil
+	c.GetCustomModelTrainingDate()
+}
+
+func TestCopilotIDEChatModel_GetCustomModelTrainingDate(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	c := &CopilotIDEChatModel{CustomModelTrainingDate: &zeroValue}
+	c.GetCustomModelTrainingDate()
+	c = &CopilotIDEChatModel{}
+	c.GetCustomModelTrainingDate()
+	c = nil
+	c.GetCustomModelTrainingDate()
+}
+
+func TestCopilotIDECodeCompletionsModel_GetCustomModelTrainingDate(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue string
+	c := &CopilotIDECodeCompletionsModel{CustomModelTrainingDate: &zeroValue}
+	c.GetCustomModelTrainingDate()
+	c = &CopilotIDECodeCompletionsModel{}
+	c.GetCustomModelTrainingDate()
+	c = nil
+	c.GetCustomModelTrainingDate()
+}
+
+func TestCopilotMetrics_GetCopilotDotcomChat(tt *testing.T) {
+	tt.Parallel()
+	c := &CopilotMetrics{}
+	c.GetCopilotDotcomChat()
+	c = nil
+	c.GetCopilotDotcomChat()
+}
+
+func TestCopilotMetrics_GetCopilotDotcomPullRequests(tt *testing.T) {
+	tt.Parallel()
+	c := &CopilotMetrics{}
+	c.GetCopilotDotcomPullRequests()
+	c = nil
+	c.GetCopilotDotcomPullRequests()
+}
+
+func TestCopilotMetrics_GetCopilotIDEChat(tt *testing.T) {
+	tt.Parallel()
+	c := &CopilotMetrics{}
+	c.GetCopilotIDEChat()
+	c = nil
+	c.GetCopilotIDEChat()
+}
+
+func TestCopilotMetrics_GetCopilotIDECodeCompletions(tt *testing.T) {
+	tt.Parallel()
+	c := &CopilotMetrics{}
+	c.GetCopilotIDECodeCompletions()
+	c = nil
+	c.GetCopilotIDECodeCompletions()
+}
+
+func TestCopilotMetrics_GetTotalActiveUsers(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue int
+	c := &CopilotMetrics{TotalActiveUsers: &zeroValue}
+	c.GetTotalActiveUsers()
+	c = &CopilotMetrics{}
+	c.GetTotalActiveUsers()
+	c = nil
+	c.GetTotalActiveUsers()
+}
+
+func TestCopilotMetrics_GetTotalEngagedUsers(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue int
+	c := &CopilotMetrics{TotalEngagedUsers: &zeroValue}
+	c.GetTotalEngagedUsers()
+	c = &CopilotMetrics{}
+	c.GetTotalEngagedUsers()
+	c = nil
+	c.GetTotalEngagedUsers()
+}
+
+func TestCopilotMetricsListOptions_GetSince(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue time.Time
+	c := &CopilotMetricsListOptions{Since: &zeroValue}
+	c.GetSince()
+	c = &CopilotMetricsListOptions{}
+	c.GetSince()
+	c = nil
+	c.GetSince()
+}
+
+func TestCopilotMetricsListOptions_GetUntil(tt *testing.T) {
+	tt.Parallel()
+	var zeroValue time.Time
+	c := &CopilotMetricsListOptions{Until: &zeroValue}
+	c.GetUntil()
+	c = &CopilotMetricsListOptions{}
+	c.GetUntil()
+	c = nil
+	c.GetUntil()
+}
+
 func TestCopilotOrganizationDetails_GetSeatBreakdown(tt *testing.T) {
 	tt.Parallel()
 	c := &CopilotOrganizationDetails{}