@backstage-community/plugin-copilot-backend 0.8.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,60 @@
1
1
  # @backstage-community/plugin-copilot-backend
2
2
 
3
+ ## 0.9.1
4
+
5
+ ### Patch Changes
6
+
7
+ - cd78d85: - Upgraded to Backstage release 1.38
8
+ - Applied migration to the [New JXS Transform](https://backstage.io/docs/tutorials/jsx-transform-migration/)
9
+ - Updated dependencies [cd78d85]
10
+ - @backstage-community/plugin-copilot-common@0.9.1
11
+
12
+ ## 0.9.0
13
+
14
+ ### Minor Changes
15
+
16
+ - bc2b3bf: Adds engagement metrics to be viewed. No personal details are currently stored.
17
+ (Like information on who hasnt been using its license).
18
+
19
+ This is done by fetching the seat billing information from Github.
20
+ [API ref](https://docs.github.com/en/rest/copilot/copilot-user-management?apiVersion=2022-11-28#list-all-copilot-seat-assignments-for-an-organization)
21
+
22
+ It then selects out the following metrics based of the billing information
23
+
24
+ - Total assigned seats
25
+ - Seats never used
26
+ (user has undefined last_activity_at property)
27
+ - Seats not used in the last 7/14/28 days
28
+ (diff between "today" and last_activity_at)
29
+
30
+ This is presented in a slightly different way since they are absolute numbers.
31
+ The following metrics are presented based on the last day of the selected period range
32
+
33
+ - Total assigned seats
34
+ - Seats never used
35
+ - Inactive seats last 7/14/28 days
36
+
37
+ The following metrics are calculated as average for the selected period range
38
+ excluding weekends (since usage usually goes down during theese days).
39
+
40
+ - Avg Total Active users
41
+ - Avg Total Engaged users
42
+ - Avg IDE Completion users
43
+ - Avg IDE Chat users
44
+ - Avg Github.com Chat users
45
+ - Avg Github.com PR users
46
+
47
+ All of the new metrics also have an own bar chart displaying this over the selected period range.
48
+ (Except seats not used in 7/14/28 days, who got a line-chart with multiple lines)
49
+
50
+ The backend has also been updated to use Octokit to fetch data instead of own implementation.
51
+ This also fixes the problem with pagination for some endpoints.
52
+
53
+ ### Patch Changes
54
+
55
+ - Updated dependencies [bc2b3bf]
56
+ - @backstage-community/plugin-copilot-common@0.9.0
57
+
3
58
  ## 0.8.0
4
59
 
5
60
  ### Minor Changes
@@ -1,18 +1,16 @@
1
1
  'use strict';
2
2
 
3
3
  var errors = require('@backstage/errors');
4
- var fetch = require('node-fetch');
4
+ var rest = require('@octokit/rest');
5
5
  var GithubUtils = require('../utils/GithubUtils.cjs.js');
6
6
 
7
- function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
8
-
9
- var fetch__default = /*#__PURE__*/_interopDefaultCompat(fetch);
10
-
11
7
  class GithubClient {
12
8
  constructor(copilotConfig, config) {
13
9
  this.copilotConfig = copilotConfig;
14
10
  this.config = config;
15
11
  }
12
+ enterpriseOctokit;
13
+ organizationOctokit;
16
14
  static async fromConfig(config) {
17
15
  const info = GithubUtils.getCopilotConfig(config);
18
16
  return new GithubClient(info, config);
@@ -20,44 +18,144 @@ class GithubClient {
20
18
  async getCredentials() {
21
19
  return await GithubUtils.getGithubCredentials(this.config, this.copilotConfig);
22
20
  }
21
+ async getOctokit(type) {
22
+ const credentials = await this.getCredentials();
23
+ const headers = credentials[type]?.headers || {};
24
+ return new rest.Octokit({
25
+ baseUrl: this.copilotConfig.apiBaseUrl,
26
+ auth: headers.Authorization?.replace("Bearer ", "") || "",
27
+ headers: {
28
+ Accept: "application/vnd.github+json",
29
+ "X-GitHub-Api-Version": "2022-11-28"
30
+ }
31
+ });
32
+ }
33
+ async getEnterpriseOctokit() {
34
+ if (!this.enterpriseOctokit) {
35
+ this.enterpriseOctokit = await this.getOctokit("enterprise");
36
+ }
37
+ return this.enterpriseOctokit;
38
+ }
39
+ async getOrganizationOctokit() {
40
+ if (!this.organizationOctokit) {
41
+ this.organizationOctokit = await this.getOctokit("organization");
42
+ }
43
+ return this.organizationOctokit;
44
+ }
23
45
  async fetchEnterpriseCopilotMetrics() {
46
+ const octokit = await this.getEnterpriseOctokit();
24
47
  const path = `/enterprises/${this.copilotConfig.enterprise}/copilot/metrics`;
25
- return this.get(path);
48
+ try {
49
+ const response = await octokit.request(`GET ${path}`);
50
+ return response.data;
51
+ } catch (error) {
52
+ throw errors.ResponseError.fromResponse(error.response || error);
53
+ }
26
54
  }
27
55
  async fetchEnterpriseTeamCopilotMetrics(teamId) {
56
+ const octokit = await this.getEnterpriseOctokit();
28
57
  const path = `/enterprises/${this.copilotConfig.enterprise}/team/${teamId}/copilot/metrics`;
29
- return this.get(path);
58
+ try {
59
+ const response = await octokit.request(`GET ${path}`);
60
+ return response.data;
61
+ } catch (error) {
62
+ throw errors.ResponseError.fromResponse(error.response || error);
63
+ }
30
64
  }
31
65
  async fetchEnterpriseTeams() {
66
+ const octokit = await this.getEnterpriseOctokit();
32
67
  const path = `/enterprises/${this.copilotConfig.enterprise}/teams`;
33
- return this.get(path);
68
+ try {
69
+ const teams = await octokit.paginate(`GET ${path}`, {
70
+ per_page: 100
71
+ // Maximum allowed per page
72
+ });
73
+ return teams;
74
+ } catch (error) {
75
+ throw errors.ResponseError.fromResponse(error.response || error);
76
+ }
77
+ }
78
+ async fetchEnterpriseSeats() {
79
+ const octokit = await this.getEnterpriseOctokit();
80
+ const path = `/enterprises/${this.copilotConfig.enterprise}/copilot/billing/seats`;
81
+ try {
82
+ const seats = await octokit.paginate(`GET ${path}`, {
83
+ per_page: 100
84
+ // Maximum allowed per page
85
+ });
86
+ return this.mergePaginationResult(seats);
87
+ } catch (error) {
88
+ throw errors.ResponseError.fromResponse(error.response || error);
89
+ }
34
90
  }
35
91
  async fetchOrganizationCopilotMetrics() {
92
+ const octokit = await this.getOrganizationOctokit();
36
93
  const path = `/orgs/${this.copilotConfig.organization}/copilot/metrics`;
37
- return this.get(path);
94
+ try {
95
+ const response = await octokit.request(`GET ${path}`);
96
+ return response.data;
97
+ } catch (error) {
98
+ throw errors.ResponseError.fromResponse(error.response || error);
99
+ }
38
100
  }
39
101
  async fetchOrganizationTeamCopilotMetrics(teamId) {
102
+ const octokit = await this.getOrganizationOctokit();
40
103
  const path = `/orgs/${this.copilotConfig.organization}/team/${teamId}/copilot/metrics`;
41
- return this.get(path);
104
+ try {
105
+ const response = await octokit.request(`GET ${path}`);
106
+ return response.data;
107
+ } catch (error) {
108
+ throw errors.ResponseError.fromResponse(error.response || error);
109
+ }
42
110
  }
43
111
  async fetchOrganizationTeams() {
112
+ const octokit = await this.getOrganizationOctokit();
44
113
  const path = `/orgs/${this.copilotConfig.organization}/teams`;
45
- return this.get(path);
114
+ try {
115
+ const teams = await octokit.paginate(`GET ${path}`, {
116
+ per_page: 100
117
+ // Maximum allowed per page
118
+ });
119
+ return teams;
120
+ } catch (error) {
121
+ throw errors.ResponseError.fromResponse(error.response || error);
122
+ }
46
123
  }
47
- async get(path) {
48
- const credentials = await this.getCredentials();
49
- const headers = path.startsWith("/enterprises") ? credentials.enterprise?.headers : credentials.organization?.headers;
50
- const response = await fetch__default.default(`${this.copilotConfig.apiBaseUrl}${path}`, {
51
- headers: {
52
- ...headers,
53
- Accept: "application/vnd.github+json",
54
- "X-GitHub-Api-Version": "2022-11-28"
55
- }
56
- });
57
- if (!response.ok) {
58
- throw await errors.ResponseError.fromResponse(response);
124
+ async fetchOrganizationSeats() {
125
+ const octokit = await this.getOrganizationOctokit();
126
+ try {
127
+ const seats = await octokit.paginate(octokit.copilot.listCopilotSeats, {
128
+ org: this.copilotConfig.organization,
129
+ per_page: 100
130
+ // Maximum allowed per page
131
+ });
132
+ return this.mergePaginationResult(seats);
133
+ } catch (error) {
134
+ throw errors.ResponseError.fromResponse(error.response || error);
135
+ }
136
+ }
137
+ /**
138
+ * This function is used to merge paginated results from the GitHub API
139
+ * that does not work as one would expect. If the api returns a object which
140
+ * contains paginated results, we get an array of the objects instead of merged data.
141
+ * So this function merges this data into one object where the property "seats" are
142
+ * merged into a single array.
143
+ * @param data
144
+ * @returns paginated result as one would expect
145
+ */
146
+ mergePaginationResult(data) {
147
+ if (data.length === 0) {
148
+ return {
149
+ total_seats: 0,
150
+ seats: []
151
+ };
59
152
  }
60
- return response.json();
153
+ const totalSeats = data[0].total_seats;
154
+ const seats = data.map((seat) => seat.seats).flat();
155
+ return {
156
+ total_seats: totalSeats,
157
+ seats
158
+ };
61
159
  }
62
160
  }
63
161
 
@@ -1 +1 @@
1
- {"version":3,"file":"GithubClient.cjs.js","sources":["../../src/client/GithubClient.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ResponseError } from '@backstage/errors';\nimport { Config } from '@backstage/config';\nimport {\n CopilotMetrics,\n TeamInfo,\n} from '@backstage-community/plugin-copilot-common';\nimport fetch from 'node-fetch';\nimport {\n CopilotConfig,\n CopilotCredentials,\n getCopilotConfig,\n getGithubCredentials,\n} from '../utils/GithubUtils';\n\ninterface GithubApi {\n fetchEnterpriseCopilotMetrics: () => Promise<CopilotMetrics[]>;\n fetchEnterpriseTeamCopilotMetrics: (\n teamId: string,\n ) => Promise<CopilotMetrics[]>;\n fetchOrganizationCopilotMetrics: () => Promise<CopilotMetrics[]>;\n fetchOrganizationTeamCopilotMetrics: (\n teamId: string,\n ) => Promise<CopilotMetrics[]>;\n\n fetchEnterpriseTeams: () => Promise<TeamInfo[]>;\n fetchOrganizationTeams: () => Promise<TeamInfo[]>;\n}\n\nexport class GithubClient implements GithubApi {\n constructor(\n private readonly copilotConfig: CopilotConfig,\n private readonly config: Config,\n ) {}\n\n static async fromConfig(config: Config) {\n const info = getCopilotConfig(config);\n return new GithubClient(info, config);\n }\n\n private async getCredentials(): Promise<CopilotCredentials> {\n return await getGithubCredentials(this.config, this.copilotConfig);\n }\n\n async fetchEnterpriseCopilotMetrics(): Promise<CopilotMetrics[]> {\n const path = `/enterprises/${this.copilotConfig.enterprise}/copilot/metrics`;\n return this.get(path);\n }\n\n async fetchEnterpriseTeamCopilotMetrics(\n teamId: string,\n ): Promise<CopilotMetrics[]> {\n const path = `/enterprises/${this.copilotConfig.enterprise}/team/${teamId}/copilot/metrics`;\n return this.get(path);\n }\n\n async fetchEnterpriseTeams(): Promise<TeamInfo[]> {\n const path = `/enterprises/${this.copilotConfig.enterprise}/teams`;\n return this.get(path);\n }\n\n async fetchOrganizationCopilotMetrics(): Promise<CopilotMetrics[]> {\n const path = `/orgs/${this.copilotConfig.organization}/copilot/metrics`;\n return this.get(path);\n }\n\n async fetchOrganizationTeamCopilotMetrics(\n teamId: string,\n ): Promise<CopilotMetrics[]> {\n const path = `/orgs/${this.copilotConfig.organization}/team/${teamId}/copilot/metrics`;\n return this.get(path);\n }\n\n async fetchOrganizationTeams(): Promise<TeamInfo[]> {\n const path = `/orgs/${this.copilotConfig.organization}/teams`;\n return this.get(path);\n }\n\n private async get<T>(path: string): Promise<T> {\n const credentials = await this.getCredentials();\n const headers = path.startsWith('/enterprises')\n ? credentials.enterprise?.headers\n : credentials.organization?.headers;\n\n const response = await fetch(`${this.copilotConfig.apiBaseUrl}${path}`, {\n headers: {\n ...headers,\n Accept: 'application/vnd.github+json',\n 'X-GitHub-Api-Version': '2022-11-28',\n },\n });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return response.json() as Promise<T>;\n }\n}\n"],"names":["getCopilotConfig","getGithubCredentials","fetch","ResponseError"],"mappings":";;;;;;;;;;AA4CO,MAAM,YAAkC,CAAA;AAAA,EAC7C,WAAA,CACmB,eACA,MACjB,EAAA;AAFiB,IAAA,IAAA,CAAA,aAAA,GAAA,aAAA;AACA,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA;AAChB,EAEH,aAAa,WAAW,MAAgB,EAAA;AACtC,IAAM,MAAA,IAAA,GAAOA,6BAAiB,MAAM,CAAA;AACpC,IAAO,OAAA,IAAI,YAAa,CAAA,IAAA,EAAM,MAAM,CAAA;AAAA;AACtC,EAEA,MAAc,cAA8C,GAAA;AAC1D,IAAA,OAAO,MAAMC,gCAAA,CAAqB,IAAK,CAAA,MAAA,EAAQ,KAAK,aAAa,CAAA;AAAA;AACnE,EAEA,MAAM,6BAA2D,GAAA;AAC/D,IAAA,MAAM,IAAO,GAAA,CAAA,aAAA,EAAgB,IAAK,CAAA,aAAA,CAAc,UAAU,CAAA,gBAAA,CAAA;AAC1D,IAAO,OAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA;AACtB,EAEA,MAAM,kCACJ,MAC2B,EAAA;AAC3B,IAAA,MAAM,OAAO,CAAgB,aAAA,EAAA,IAAA,CAAK,aAAc,CAAA,UAAU,SAAS,MAAM,CAAA,gBAAA,CAAA;AACzE,IAAO,OAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA;AACtB,EAEA,MAAM,oBAA4C,GAAA;AAChD,IAAA,MAAM,IAAO,GAAA,CAAA,aAAA,EAAgB,IAAK,CAAA,aAAA,CAAc,UAAU,CAAA,MAAA,CAAA;AAC1D,IAAO,OAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA;AACtB,EAEA,MAAM,+BAA6D,GAAA;AACjE,IAAA,MAAM,IAAO,GAAA,CAAA,MAAA,EAAS,IAAK,CAAA,aAAA,CAAc,YAAY,CAAA,gBAAA,CAAA;AACrD,IAAO,OAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA;AACtB,EAEA,MAAM,oCACJ,MAC2B,EAAA;AAC3B,IAAA,MAAM,OAAO,CAAS,MAAA,EAAA,IAAA,CAAK,aAAc,CAAA,YAAY,SAAS,MAAM,CAAA,gBAAA,CAAA;AACpE,IAAO,OAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA;AACtB,EAEA,MAAM,sBAA8C,GAAA;AAClD,IAAA,MAAM,IAAO,GAAA,CAAA,MAAA,EAAS,IAAK,CAAA,aAAA,CAAc,YAAY,CAAA,MAAA,CAAA;AACrD,IAAO,OAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA;AACtB,EAEA,MAAc,IAAO,IAA0B,EAAA;AAC7C,IAAM,MAAA,WAAA,GAAc,MAAM,IAAA,CAAK,cAAe,EAAA;AAC9C,IAAM,MAAA,OAAA,GAAU,KAAK,UAAW,CAAA,cAAc,IAC1C,WAAY,CAAA,UAAA,EAAY,OACxB,GAAA,WAAA,CAAY,YAAc,EAAA,OAAA;AAE9B,IAAM,MAAA,QAAA,GAAW,MAAMC,sBAAM,CAAA,CAAA,EAAG,KAAK,aAAc,CAAA,UAAU,CAAG,EAAA,IAAI,CAAI,CAAA,EAAA;AAAA,MACtE,OAAS,EAAA;AAAA,QACP,GAAG,OAAA;AAAA,QACH,MAAQ,EAAA,6BAAA;AAAA,QACR,sBAAwB,EAAA;AAAA;AAC1B,KACD,CAAA;AAED,IAAI,IAAA,CAAC,SAAS,EAAI,EAAA;AAChB,MAAM,MAAA,MAAMC,oBAAc,CAAA,YAAA,CAAa,QAAQ,CAAA;AAAA;AAGjD,IAAA,OAAO,SAAS,IAAK,EAAA;AAAA;AAEzB;;;;"}
1
+ {"version":3,"file":"GithubClient.cjs.js","sources":["../../src/client/GithubClient.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ResponseError } from '@backstage/errors';\nimport { Config } from '@backstage/config';\nimport {\n CopilotMetrics,\n CopilotSeats,\n TeamInfo,\n} from '@backstage-community/plugin-copilot-common';\nimport { Octokit } from '@octokit/rest';\nimport {\n CopilotConfig,\n CopilotCredentials,\n getCopilotConfig,\n getGithubCredentials,\n} from '../utils/GithubUtils';\n\ninterface GithubApi {\n fetchEnterpriseCopilotMetrics: () => Promise<CopilotMetrics[]>;\n fetchEnterpriseTeamCopilotMetrics: (\n teamId: string,\n ) => Promise<CopilotMetrics[]>;\n fetchOrganizationCopilotMetrics: () => Promise<CopilotMetrics[]>;\n fetchOrganizationTeamCopilotMetrics: (\n teamId: string,\n ) => Promise<CopilotMetrics[]>;\n\n fetchEnterpriseTeams: () => Promise<TeamInfo[]>;\n fetchOrganizationTeams: () => Promise<TeamInfo[]>;\n}\n\nexport class GithubClient implements GithubApi {\n private enterpriseOctokit?: Octokit;\n private organizationOctokit?: Octokit;\n\n constructor(\n private readonly copilotConfig: CopilotConfig,\n private readonly config: Config,\n ) {}\n\n static async fromConfig(config: Config) {\n const info = getCopilotConfig(config);\n return new GithubClient(info, config);\n }\n\n private async getCredentials(): Promise<CopilotCredentials> {\n return await getGithubCredentials(this.config, this.copilotConfig);\n }\n\n private async getOctokit(\n type: 'enterprise' | 'organization',\n ): Promise<Octokit> {\n const credentials = await this.getCredentials();\n const headers = credentials[type]?.headers || {};\n\n return new Octokit({\n baseUrl: this.copilotConfig.apiBaseUrl,\n auth: headers.Authorization?.replace('Bearer ', '') || '',\n headers: {\n Accept: 'application/vnd.github+json',\n 'X-GitHub-Api-Version': '2022-11-28',\n },\n });\n }\n\n private async getEnterpriseOctokit(): Promise<Octokit> {\n if (!this.enterpriseOctokit) {\n this.enterpriseOctokit = await this.getOctokit('enterprise');\n }\n return this.enterpriseOctokit;\n }\n\n private async getOrganizationOctokit(): Promise<Octokit> {\n if (!this.organizationOctokit) {\n this.organizationOctokit = await this.getOctokit('organization');\n }\n return this.organizationOctokit;\n }\n\n async fetchEnterpriseCopilotMetrics(): Promise<CopilotMetrics[]> {\n const octokit = await this.getEnterpriseOctokit();\n const path = `/enterprises/${this.copilotConfig.enterprise}/copilot/metrics`;\n\n try {\n const response = await octokit.request(`GET ${path}`);\n return response.data as CopilotMetrics[];\n } catch (error) {\n throw ResponseError.fromResponse(error.response || error);\n }\n }\n\n async fetchEnterpriseTeamCopilotMetrics(\n teamId: string,\n ): Promise<CopilotMetrics[]> {\n const octokit = await this.getEnterpriseOctokit();\n const path = `/enterprises/${this.copilotConfig.enterprise}/team/${teamId}/copilot/metrics`;\n\n try {\n const response = await octokit.request(`GET ${path}`);\n return response.data as CopilotMetrics[];\n } catch (error) {\n throw ResponseError.fromResponse(error.response || error);\n }\n }\n\n async fetchEnterpriseTeams(): Promise<TeamInfo[]> {\n const octokit = await this.getEnterpriseOctokit();\n const path = `/enterprises/${this.copilotConfig.enterprise}/teams`;\n\n try {\n const teams = await octokit.paginate(`GET ${path}`, {\n per_page: 100, // Maximum allowed per page\n });\n return teams as TeamInfo[];\n } catch (error) {\n throw ResponseError.fromResponse(error.response || error);\n }\n }\n\n async fetchEnterpriseSeats(): Promise<any> {\n const octokit = await this.getEnterpriseOctokit();\n const path = `/enterprises/${this.copilotConfig.enterprise}/copilot/billing/seats`;\n\n try {\n const seats = await octokit.paginate(`GET ${path}`, {\n per_page: 100, // Maximum allowed per page\n });\n\n return this.mergePaginationResult(seats as CopilotSeats[]);\n } catch (error) {\n throw ResponseError.fromResponse(error.response || error);\n }\n }\n\n async fetchOrganizationCopilotMetrics(): Promise<CopilotMetrics[]> {\n const octokit = await this.getOrganizationOctokit();\n const path = `/orgs/${this.copilotConfig.organization}/copilot/metrics`;\n\n try {\n const response = await octokit.request(`GET ${path}`);\n return response.data as CopilotMetrics[];\n } catch (error) {\n throw ResponseError.fromResponse(error.response || error);\n }\n }\n\n async fetchOrganizationTeamCopilotMetrics(\n teamId: string,\n ): Promise<CopilotMetrics[]> {\n const octokit = await this.getOrganizationOctokit();\n const path = `/orgs/${this.copilotConfig.organization}/team/${teamId}/copilot/metrics`;\n\n try {\n const response = await octokit.request(`GET ${path}`);\n return response.data as CopilotMetrics[];\n } catch (error) {\n throw ResponseError.fromResponse(error.response || error);\n }\n }\n\n async fetchOrganizationTeams(): Promise<TeamInfo[]> {\n const octokit = await this.getOrganizationOctokit();\n const path = `/orgs/${this.copilotConfig.organization}/teams`;\n\n try {\n const teams = await octokit.paginate(`GET ${path}`, {\n per_page: 100, // Maximum allowed per page\n });\n return teams as TeamInfo[];\n } catch (error) {\n throw ResponseError.fromResponse(error.response || error);\n }\n }\n\n async fetchOrganizationSeats(): Promise<CopilotSeats> {\n const octokit = await this.getOrganizationOctokit();\n\n try {\n const seats = await octokit.paginate(octokit.copilot.listCopilotSeats, {\n org: this.copilotConfig.organization!,\n per_page: 100, // Maximum allowed per page\n });\n\n return this.mergePaginationResult(seats as CopilotSeats[]);\n } catch (error) {\n throw ResponseError.fromResponse(error.response || error);\n }\n }\n\n /**\n * This function is used to merge paginated results from the GitHub API\n * that does not work as one would expect. If the api returns a object which\n * contains paginated results, we get an array of the objects instead of merged data.\n * So this function merges this data into one object where the property \"seats\" are\n * merged into a single array.\n * @param data\n * @returns paginated result as one would expect\n */\n mergePaginationResult(data: CopilotSeats[]): CopilotSeats {\n if (data.length === 0) {\n return {\n total_seats: 0,\n seats: [],\n };\n }\n\n // total_seats is the same for all pages, so we can just take it from the first page\n // and merge the seats from all pages into one array\n const totalSeats = data[0].total_seats;\n const seats = data.map(seat => seat.seats).flat();\n\n return {\n total_seats: totalSeats,\n seats: seats,\n };\n }\n}\n"],"names":["getCopilotConfig","getGithubCredentials","Octokit","ResponseError"],"mappings":"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"}
@@ -76,6 +76,9 @@ class DatabaseHandler {
76
76
  async batchInsertIdeChatEditorModels(metrics) {
77
77
  await this.db("ide_chat_editors_model").insert(metrics).onConflict(["day", "type", "team_name", "editor", "model"]).ignore();
78
78
  }
79
+ async insertSeatAnalysys(metric) {
80
+ await this.db("seats").insert(metric).onConflict(["day", "type", "team_name"]).ignore();
81
+ }
79
82
  async getMostRecentDayFromMetrics(type, teamName) {
80
83
  try {
81
84
  const query = await this.db("metrics").where("type", type).where("team_name", teamName ?? null).orderBy("day", "desc").first("day");
@@ -108,6 +111,76 @@ class DatabaseHandler {
108
111
  }
109
112
  return this.db("metrics").where("type", type).whereNull("team_name").whereBetween("day", [startDate, endDate]);
110
113
  }
114
+ async getSeatMetrics(startDate, endDate, type, teamName) {
115
+ return await this.db("seats").where("type", type).where("team_name", teamName ?? "").whereBetween("day", [startDate, endDate]).orderBy("day", "asc");
116
+ }
117
+ async getEngagementMetrics(startDate, endDate, type, teamName) {
118
+ let query = this.db("copilot_metrics as cm").select(
119
+ "cm.day",
120
+ "cm.type",
121
+ "cm.team_name",
122
+ this.db.raw(
123
+ "CAST(MIN(cm.total_active_users) AS INTEGER) as total_active_users"
124
+ ),
125
+ this.db.raw(
126
+ "CAST(MIN(cm.total_engaged_users) AS INTEGER) as total_engaged_users"
127
+ ),
128
+ this.db.raw(
129
+ "CAST(MIN(ide_completions.total_engaged_users) AS INTEGER) as ide_completions_engaged_users"
130
+ ),
131
+ this.db.raw(
132
+ "CAST(MIN(ide_chats.total_engaged_users) AS INTEGER) as ide_chats_engaged_users"
133
+ ),
134
+ this.db.raw(
135
+ "CAST(MIN(dotcom_chats.total_engaged_users) AS INTEGER) as dotcom_chats_engaged_users"
136
+ ),
137
+ this.db.raw(
138
+ "CAST(MIN(dotcom_prs.total_engaged_users) AS INTEGER) as dotcom_prs_engaged_users"
139
+ )
140
+ ).leftJoin("ide_completions", (join) => {
141
+ join.on("ide_completions.day", "=", "cm.day").andOn("ide_completions.type", "=", "cm.type");
142
+ if (teamName) {
143
+ join.andOn(
144
+ "ide_completions.team_name",
145
+ "=",
146
+ this.db.raw("?", [teamName])
147
+ );
148
+ } else {
149
+ join.andOnNull("ide_completions.team_name");
150
+ }
151
+ }).leftJoin("ide_chats", (join) => {
152
+ join.on("ide_chats.day", "=", "cm.day").andOn("ide_chats.type", "=", "cm.type");
153
+ if (teamName) {
154
+ join.andOn("ide_chats.team_name", "=", this.db.raw("?", [teamName]));
155
+ } else {
156
+ join.andOnNull("ide_chats.team_name");
157
+ }
158
+ }).leftJoin("dotcom_chats", (join) => {
159
+ join.on("dotcom_chats.day", "=", "cm.day").andOn("dotcom_chats.type", "=", "cm.type");
160
+ if (teamName) {
161
+ join.andOn(
162
+ "dotcom_chats.team_name",
163
+ "=",
164
+ this.db.raw("?", [teamName])
165
+ );
166
+ } else {
167
+ join.andOnNull("dotcom_chats.team_name");
168
+ }
169
+ }).leftJoin("dotcom_prs", (join) => {
170
+ join.on("dotcom_prs.day", "=", "cm.day").andOn("dotcom_prs.type", "=", "cm.type");
171
+ if (teamName) {
172
+ join.andOn("dotcom_prs.team_name", "=", this.db.raw("?", [teamName]));
173
+ } else {
174
+ join.andOnNull("dotcom_prs.team_name");
175
+ }
176
+ }).where("cm.type", type).whereBetween("cm.day", [startDate, endDate]).groupBy("cm.day", "cm.type", "cm.team_name").orderBy("cm.day", "asc");
177
+ if (teamName) {
178
+ query = query.where("cm.team_name", teamName);
179
+ } else {
180
+ query = query.whereNull("cm.team_name");
181
+ }
182
+ return await query;
183
+ }
111
184
  async getMetricsV2(startDate, endDate, type, teamName) {
112
185
  let query = this.db("copilot_metrics as cm").select(
113
186
  "cm.day",
@@ -1 +1 @@
1
- {"version":3,"file":"DatabaseHandler.cjs.js","sources":["../../src/db/DatabaseHandler.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n resolvePackagePath,\n DatabaseService,\n} from '@backstage/backend-plugin-api';\nimport {\n Metric,\n MetricsType,\n PeriodRange,\n CopilotIdeCodeCompletions,\n CopilotIdeLanguages,\n CopilotMetrics,\n CopilotEditors,\n CopilotModels,\n CopilotLanguages,\n CopilotChats,\n CopilotChatEditors,\n CopilotChatModels,\n} from '@backstage-community/plugin-copilot-common';\nimport { Knex } from 'knex';\n\nconst migrationsDir = resolvePackagePath(\n '@backstage-community/plugin-copilot-backend',\n 'migrations',\n);\n\ntype Options = {\n database: DatabaseService;\n};\n\nexport type Breakdown = {\n day: string;\n editor: string;\n language: string;\n lines_accepted: number;\n lines_suggested: number;\n suggestions_count: number;\n acceptances_count: number;\n active_users: number;\n};\n\nexport type CopilotMetricsDb = Omit<\n CopilotMetrics,\n | 'date'\n | 'copilot_ide_code_completions'\n | 'copilot_ide_chat'\n | 'copilot_dotcom_chat'\n | 'copilot_dotcom_pull_requests'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n};\n\nexport type CopilotIdeCodeCompletionsDb = Omit<\n CopilotIdeCodeCompletions,\n 'editors' | 'languages'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n};\n\nexport type CopilotIdeCodeCompletionsLanguageDb = Omit<\n CopilotIdeLanguages,\n 'day' | 'name' | 'language'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n language: string;\n};\n\nexport type CopilotIdeCodeCompletionsEditorsDb = Omit<\n CopilotEditors,\n 'day' | 'name' | 'models'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n editor: string;\n};\n\nexport type CopilotIdeChatsDb = Omit<CopilotChats, 'day' | 'editors'> & {\n day: string;\n};\n\nexport type CopilotIdeChatsEditorsDb = Omit<\n CopilotChatEditors,\n 'day' | 'name' | 'models' | 'editor'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n editor: string;\n};\n\nexport type CopilotIdeChatsEditorModelDb = Omit<\n CopilotChatModels,\n 'name' | 'day' | 'model' | 'editor'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n editor: string;\n model: string;\n};\n\nexport type CopilotIdeCodeCompletionsEditorModelsDb = Omit<\n CopilotModels,\n 'day' | 'editor' | 'model' | 'name' | 'languages'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n editor: string;\n model: string;\n};\n\nexport type CopilotIdeCodeCompletionsEditorModelLanguagesDb = Omit<\n CopilotLanguages,\n 'day' | 'editor' | 'model' | 'name' | 'language'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n editor: string;\n model: string;\n language: string;\n};\n\nexport type MetricDbRow = Omit<Metric, 'breakdown'> & {\n breakdown: string;\n};\n\nexport class DatabaseHandler {\n static async create(options: Options): Promise<DatabaseHandler> {\n const { database } = options;\n const client = await database.getClient();\n\n if (!database.migrations?.skip) {\n await client.migrate.latest({\n directory: migrationsDir,\n });\n }\n\n return new DatabaseHandler(client);\n }\n\n private constructor(private readonly db: Knex) {}\n\n async getPeriodRange(type: MetricsType): Promise<PeriodRange | undefined> {\n const query = this.db<MetricDbRow>('metrics').where('type', type);\n\n const minDate = await query.orderBy('day', 'asc').first('day');\n const maxDate = await query.orderBy('day', 'desc').first('day');\n\n if (!minDate?.day || !maxDate?.day) return undefined;\n\n return { minDate: minDate.day, maxDate: maxDate.day };\n }\n\n async getPeriodRangeV2(type: MetricsType): Promise<PeriodRange | undefined> {\n const query = this.db('copilot_metrics').where('type', type);\n\n const minDate = await query.orderBy('day', 'asc').first('day');\n const maxDate = await query.orderBy('day', 'desc').first('day');\n\n if (!minDate?.day || !maxDate?.day) return undefined;\n\n return { minDate: minDate.day, maxDate: maxDate.day };\n }\n\n async getTeams(\n type: MetricsType,\n startDate: string,\n endDate: string,\n ): Promise<Array<string | undefined>> {\n const result = await this.db<MetricDbRow>('copilot_metrics')\n .where('type', type)\n .whereBetween('day', [startDate, endDate])\n .whereNotNull('team_name')\n .distinct('team_name')\n .orderBy('team_name', 'asc')\n .select('team_name');\n\n return result.map(x => x.team_name);\n }\n\n async batchInsert(metrics: MetricDbRow[]): Promise<void> {\n await this.db<MetricDbRow[]>('metrics')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name'])\n .ignore();\n }\n\n async batchInsertMetrics(metrics: CopilotMetricsDb[]): Promise<void> {\n await this.db<CopilotMetricsDb[]>('copilot_metrics')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name'])\n .ignore();\n }\n\n async batchInsertIdeCompletions(\n metrics: CopilotIdeCodeCompletionsDb[],\n ): Promise<void> {\n await this.db<CopilotIdeCodeCompletionsDb[]>('ide_completions')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name'])\n .ignore();\n }\n\n async batchInsertIdeCompletionsLanguages(\n metrics: CopilotIdeCodeCompletionsLanguageDb[],\n ): Promise<void> {\n await this.db<CopilotIdeCodeCompletionsLanguageDb[]>(\n 'ide_completions_language_users',\n )\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'language'])\n .ignore();\n }\n\n async batchInsertIdeCompletionsEditors(\n metrics: CopilotIdeCodeCompletionsEditorsDb[],\n ): Promise<void> {\n await this.db<CopilotIdeCodeCompletionsEditorsDb[]>(\n 'ide_completions_language_editors',\n )\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'editor'])\n .ignore();\n }\n\n async batchInsertIdeCompletionsEditorModels(\n metrics: CopilotIdeCodeCompletionsEditorModelsDb[],\n ): Promise<void> {\n await this.db<CopilotIdeCodeCompletionsEditorModelsDb[]>(\n 'ide_completions_language_editors_model',\n )\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'editor', 'model'])\n .ignore();\n }\n\n async batchInsertIdeCompletionsEditorModelLanguages(\n metrics: CopilotIdeCodeCompletionsEditorModelLanguagesDb[],\n ): Promise<void> {\n await this.db<CopilotIdeCodeCompletionsEditorModelLanguagesDb[]>(\n 'ide_completions_language_editors_model_language',\n )\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'editor', 'model', 'language'])\n .ignore();\n }\n\n async batchInsertIdeChats(metrics: CopilotIdeChatsDb[]): Promise<void> {\n await this.db<CopilotIdeChatsDb[]>('ide_chats')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name'])\n .ignore();\n }\n\n async batchInsertIdeChatEditors(\n metrics: CopilotIdeChatsEditorsDb[],\n ): Promise<void> {\n await this.db<CopilotIdeChatsEditorsDb[]>('ide_chat_editors')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'editor'])\n .ignore();\n }\n\n async batchInsertIdeChatEditorModels(\n metrics: CopilotIdeChatsEditorModelDb[],\n ): Promise<void> {\n await this.db<CopilotIdeChatsEditorModelDb[]>('ide_chat_editors_model')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'editor', 'model'])\n .ignore();\n }\n\n async getMostRecentDayFromMetrics(\n type: MetricsType,\n teamName?: string,\n ): Promise<string | undefined> {\n try {\n const query = await this.db<MetricDbRow>('metrics')\n .where('type', type)\n .where('team_name', teamName ?? null)\n .orderBy('day', 'desc')\n .first('day');\n return query ? query.day : undefined;\n } catch (e) {\n return undefined;\n }\n }\n\n async getMostRecentDayFromMetricsV2(\n type: MetricsType,\n teamName?: string,\n ): Promise<string | undefined> {\n try {\n const query = this.db('copilot_metrics')\n .where('type', type)\n .where('team_name', teamName ?? null)\n .orderBy('day', 'desc')\n .first('day');\n const res = await query;\n return res ? res.day : undefined;\n } catch (e) {\n return undefined;\n }\n }\n\n async getEarliestDayFromMetricsV2(\n type: MetricsType,\n teamName?: string,\n ): Promise<string | undefined> {\n try {\n const query = this.db('copilot_metrics')\n .where('type', type)\n .where('team_name', teamName ?? null)\n .orderBy('day', 'asc')\n .first('day');\n const res = await query;\n return res ? res.day : undefined;\n } catch (e) {\n return undefined;\n }\n }\n\n async getMetrics(\n startDate: string,\n endDate: string,\n type: MetricsType,\n teamName?: string,\n ): Promise<MetricDbRow[]> {\n if (teamName) {\n return await this.db<MetricDbRow>('metrics')\n .where('type', type)\n .where('team_name', teamName)\n .whereBetween('day', [startDate, endDate]);\n }\n return this.db<MetricDbRow>('metrics')\n .where('type', type)\n .whereNull('team_name')\n .whereBetween('day', [startDate, endDate]);\n }\n\n async getMetricsV2(\n startDate: string,\n endDate: string,\n type: MetricsType,\n teamName?: string,\n ): Promise<MetricDbRow[]> {\n let query = this.db('copilot_metrics as cm')\n .select(\n 'cm.day',\n 'cm.type',\n 'cm.team_name',\n this.db.raw(\n 'CAST(MIN(cm.total_active_users) AS INTEGER) as total_active_users',\n ),\n this.db.raw(\n 'CAST(MIN(ide_chats.total_engaged_users) AS INTEGER) as total_active_chat_users',\n ),\n this.db.raw(\n 'CAST(SUM(icelm.total_code_suggestions) AS INTEGER) as total_suggestions_count',\n ),\n this.db.raw(\n 'CAST(SUM(icelm.total_code_acceptances) AS INTEGER) as total_acceptances_count',\n ),\n this.db.raw(\n 'CAST(SUM(icelm.total_code_lines_suggested) AS INTEGER) as total_lines_suggested',\n ),\n this.db.raw(\n 'CAST(SUM(icelm.total_code_lines_accepted) AS INTEGER) as total_lines_accepted',\n ),\n this.db.raw(\n 'CAST(SUM(icem.total_chats) AS INTEGER) as total_chat_turns',\n ),\n this.db.raw(\n 'CAST(SUM(icem.total_chat_copy_events) AS INTEGER) as total_chat_acceptances',\n ),\n this.db.raw(\"'' as breakdown\"),\n )\n .join('ide_completions', join => {\n join\n .on('ide_completions.day', '=', 'cm.day')\n .andOn('ide_completions.type', '=', 'cm.type');\n if (teamName) {\n join.andOn(\n 'ide_completions.team_name',\n '=',\n this.db.raw('?', [teamName]),\n );\n } else {\n join.andOnNull('ide_completions.team_name');\n }\n })\n .join('ide_chats', join => {\n join\n .on('ide_chats.day', '=', 'cm.day')\n .andOn('ide_chats.type', '=', 'cm.type');\n if (teamName) {\n join.andOn('ide_chats.team_name', '=', this.db.raw('?', [teamName]));\n } else {\n join.andOnNull('ide_chats.team_name');\n }\n })\n .join(\n this.db.raw(\n `(SELECT day, type, team_name,\n SUM(total_code_suggestions) as total_code_suggestions, \n SUM(total_code_acceptances) as total_code_acceptances, \n SUM(total_code_lines_suggested) as total_code_lines_suggested, \n SUM(total_code_lines_accepted) as total_code_lines_accepted \n FROM ide_completions_language_editors_model_language GROUP BY day, type, team_name) \n as icelm`,\n ),\n join => {\n join\n .on('icelm.day', '=', 'cm.day')\n .andOn('icelm.type', '=', 'cm.type');\n if (teamName) {\n join.andOn('icelm.team_name', '=', this.db.raw('?', [teamName]));\n } else {\n join.andOnNull('icelm.team_name');\n }\n },\n )\n .join(\n this.db.raw(\n `(SELECT day, type, team_name, SUM(total_chats) as total_chats, \n SUM(total_chat_copy_events) as total_chat_copy_events \n FROM ide_chat_editors_model GROUP BY day, type, team_name) as icem`,\n ),\n join => {\n join.on('icem.day', '=', 'cm.day').andOn('icem.type', '=', 'cm.type');\n if (teamName) {\n join.andOn('icem.team_name', '=', this.db.raw('?', [teamName]));\n } else {\n join.andOnNull('icem.team_name');\n }\n },\n )\n .where('cm.type', type)\n .whereBetween('cm.day', [startDate, endDate])\n .groupBy('cm.day', 'cm.type', 'cm.team_name')\n .orderBy('cm.day', 'asc');\n\n if (teamName) {\n query = query.where('cm.team_name', teamName);\n } else {\n query = query.whereNull('cm.team_name');\n }\n\n return await query;\n }\n\n async getBreakdown(\n startDate: string,\n endDate: string,\n type: MetricsType,\n teamName?: string,\n ): Promise<Breakdown[]> {\n let query = this.db<Breakdown>('copilot_metrics as cm')\n .select(\n 'cm.day',\n 'icleml.editor as editor',\n 'icleml.language as language',\n this.db.raw(\n 'CAST(SUM(icleml.total_engaged_users) AS INTEGER) as active_users',\n ),\n this.db.raw(\n 'CAST(SUM(icleml.total_code_lines_suggested) AS INTEGER) as lines_suggested',\n ),\n this.db.raw(\n 'CAST(SUM(icleml.total_code_lines_accepted) AS INTEGER) as lines_accepted',\n ),\n this.db.raw(\n 'CAST(SUM(icleml.total_code_suggestions) AS INTEGER) as suggestions_count',\n ),\n this.db.raw(\n 'CAST(SUM(icleml.total_code_acceptances) AS INTEGER) as acceptances_count',\n ),\n )\n .join(\n 'ide_completions_language_editors_model_language as icleml',\n join => {\n join\n .on('icleml.day', '=', 'cm.day')\n .andOn('icleml.type', '=', 'cm.type');\n if (teamName) {\n join.andOn('icleml.team_name', '=', this.db.raw('?', [teamName]));\n } else {\n join.andOnNull('icleml.team_name');\n }\n },\n )\n .whereBetween('cm.day', [startDate, endDate])\n .where('icleml.model', 'default')\n .where('cm.type', type)\n .groupBy('cm.day', 'icleml.editor', 'icleml.language')\n .orderBy('cm.day', 'asc');\n\n if (teamName) {\n query = query.where('cm.team_name', teamName);\n } else {\n query = query.whereNull('cm.team_name');\n }\n\n return await query;\n }\n}\n"],"names":["resolvePackagePath"],"mappings":"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"}
1
+ {"version":3,"file":"DatabaseHandler.cjs.js","sources":["../../src/db/DatabaseHandler.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n resolvePackagePath,\n DatabaseService,\n} from '@backstage/backend-plugin-api';\nimport {\n Metric,\n MetricsType,\n PeriodRange,\n CopilotIdeCodeCompletions,\n CopilotIdeLanguages,\n CopilotMetrics,\n CopilotEditors,\n CopilotModels,\n CopilotLanguages,\n CopilotChats,\n CopilotChatEditors,\n CopilotChatModels,\n EngagementMetrics,\n SeatAnalysis,\n} from '@backstage-community/plugin-copilot-common';\nimport { Knex } from 'knex';\n\nconst migrationsDir = resolvePackagePath(\n '@backstage-community/plugin-copilot-backend',\n 'migrations',\n);\n\ntype Options = {\n database: DatabaseService;\n};\n\nexport type Breakdown = {\n day: string;\n editor: string;\n language: string;\n lines_accepted: number;\n lines_suggested: number;\n suggestions_count: number;\n acceptances_count: number;\n active_users: number;\n};\n\nexport type CopilotMetricsDb = Omit<\n CopilotMetrics,\n | 'date'\n | 'copilot_ide_code_completions'\n | 'copilot_ide_chat'\n | 'copilot_dotcom_chat'\n | 'copilot_dotcom_pull_requests'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n};\n\nexport type CopilotIdeCodeCompletionsDb = Omit<\n CopilotIdeCodeCompletions,\n 'editors' | 'languages'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n};\n\nexport type CopilotIdeCodeCompletionsLanguageDb = Omit<\n CopilotIdeLanguages,\n 'day' | 'name' | 'language'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n language: string;\n};\n\nexport type CopilotIdeCodeCompletionsEditorsDb = Omit<\n CopilotEditors,\n 'day' | 'name' | 'models'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n editor: string;\n};\n\nexport type CopilotIdeChatsDb = Omit<CopilotChats, 'day' | 'editors'> & {\n day: string;\n};\n\nexport type CopilotIdeChatsEditorsDb = Omit<\n CopilotChatEditors,\n 'day' | 'name' | 'models' | 'editor'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n editor: string;\n};\n\nexport type CopilotIdeChatsEditorModelDb = Omit<\n CopilotChatModels,\n 'name' | 'day' | 'model' | 'editor'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n editor: string;\n model: string;\n};\n\nexport type CopilotIdeCodeCompletionsEditorModelsDb = Omit<\n CopilotModels,\n 'day' | 'editor' | 'model' | 'name' | 'languages'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n editor: string;\n model: string;\n};\n\nexport type CopilotIdeCodeCompletionsEditorModelLanguagesDb = Omit<\n CopilotLanguages,\n 'day' | 'editor' | 'model' | 'name' | 'language'\n> & {\n day: string;\n /**\n * The type of the metrics data.\n * Can be 'enterprise', 'organization'.\n */\n type: MetricsType;\n\n /**\n * The name of the team, applicable when the metric is for a specific team.\n * When null, it indicates metrics for all teams, aggregated at the 'enterprise' or 'organization' level.\n */\n team_name?: string;\n editor: string;\n model: string;\n language: string;\n};\n\nexport type MetricDbRow = Omit<Metric, 'breakdown'> & {\n breakdown: string;\n};\n\nexport class DatabaseHandler {\n static async create(options: Options): Promise<DatabaseHandler> {\n const { database } = options;\n const client = await database.getClient();\n\n if (!database.migrations?.skip) {\n await client.migrate.latest({\n directory: migrationsDir,\n });\n }\n\n return new DatabaseHandler(client);\n }\n\n private constructor(private readonly db: Knex) {}\n\n async getPeriodRange(type: MetricsType): Promise<PeriodRange | undefined> {\n const query = this.db<MetricDbRow>('metrics').where('type', type);\n\n const minDate = await query.orderBy('day', 'asc').first('day');\n const maxDate = await query.orderBy('day', 'desc').first('day');\n\n if (!minDate?.day || !maxDate?.day) return undefined;\n\n return { minDate: minDate.day, maxDate: maxDate.day };\n }\n\n async getPeriodRangeV2(type: MetricsType): Promise<PeriodRange | undefined> {\n const query = this.db('copilot_metrics').where('type', type);\n\n const minDate = await query.orderBy('day', 'asc').first('day');\n const maxDate = await query.orderBy('day', 'desc').first('day');\n\n if (!minDate?.day || !maxDate?.day) return undefined;\n\n return { minDate: minDate.day, maxDate: maxDate.day };\n }\n\n async getTeams(\n type: MetricsType,\n startDate: string,\n endDate: string,\n ): Promise<Array<string | undefined>> {\n const result = await this.db<MetricDbRow>('copilot_metrics')\n .where('type', type)\n .whereBetween('day', [startDate, endDate])\n .whereNotNull('team_name')\n .distinct('team_name')\n .orderBy('team_name', 'asc')\n .select('team_name');\n\n return result.map(x => x.team_name);\n }\n\n async batchInsert(metrics: MetricDbRow[]): Promise<void> {\n await this.db<MetricDbRow[]>('metrics')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name'])\n .ignore();\n }\n\n async batchInsertMetrics(metrics: CopilotMetricsDb[]): Promise<void> {\n await this.db<CopilotMetricsDb[]>('copilot_metrics')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name'])\n .ignore();\n }\n\n async batchInsertIdeCompletions(\n metrics: CopilotIdeCodeCompletionsDb[],\n ): Promise<void> {\n await this.db<CopilotIdeCodeCompletionsDb[]>('ide_completions')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name'])\n .ignore();\n }\n\n async batchInsertIdeCompletionsLanguages(\n metrics: CopilotIdeCodeCompletionsLanguageDb[],\n ): Promise<void> {\n await this.db<CopilotIdeCodeCompletionsLanguageDb[]>(\n 'ide_completions_language_users',\n )\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'language'])\n .ignore();\n }\n\n async batchInsertIdeCompletionsEditors(\n metrics: CopilotIdeCodeCompletionsEditorsDb[],\n ): Promise<void> {\n await this.db<CopilotIdeCodeCompletionsEditorsDb[]>(\n 'ide_completions_language_editors',\n )\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'editor'])\n .ignore();\n }\n\n async batchInsertIdeCompletionsEditorModels(\n metrics: CopilotIdeCodeCompletionsEditorModelsDb[],\n ): Promise<void> {\n await this.db<CopilotIdeCodeCompletionsEditorModelsDb[]>(\n 'ide_completions_language_editors_model',\n )\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'editor', 'model'])\n .ignore();\n }\n\n async batchInsertIdeCompletionsEditorModelLanguages(\n metrics: CopilotIdeCodeCompletionsEditorModelLanguagesDb[],\n ): Promise<void> {\n await this.db<CopilotIdeCodeCompletionsEditorModelLanguagesDb[]>(\n 'ide_completions_language_editors_model_language',\n )\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'editor', 'model', 'language'])\n .ignore();\n }\n\n async batchInsertIdeChats(metrics: CopilotIdeChatsDb[]): Promise<void> {\n await this.db<CopilotIdeChatsDb[]>('ide_chats')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name'])\n .ignore();\n }\n\n async batchInsertIdeChatEditors(\n metrics: CopilotIdeChatsEditorsDb[],\n ): Promise<void> {\n await this.db<CopilotIdeChatsEditorsDb[]>('ide_chat_editors')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'editor'])\n .ignore();\n }\n\n async batchInsertIdeChatEditorModels(\n metrics: CopilotIdeChatsEditorModelDb[],\n ): Promise<void> {\n await this.db<CopilotIdeChatsEditorModelDb[]>('ide_chat_editors_model')\n .insert(metrics)\n .onConflict(['day', 'type', 'team_name', 'editor', 'model'])\n .ignore();\n }\n\n async insertSeatAnalysys(metric: SeatAnalysis): Promise<void> {\n await this.db<SeatAnalysis>('seats')\n .insert(metric)\n .onConflict(['day', 'type', 'team_name'])\n .ignore();\n }\n\n async getMostRecentDayFromMetrics(\n type: MetricsType,\n teamName?: string,\n ): Promise<string | undefined> {\n try {\n const query = await this.db<MetricDbRow>('metrics')\n .where('type', type)\n .where('team_name', teamName ?? null)\n .orderBy('day', 'desc')\n .first('day');\n return query ? query.day : undefined;\n } catch (e) {\n return undefined;\n }\n }\n\n async getMostRecentDayFromMetricsV2(\n type: MetricsType,\n teamName?: string,\n ): Promise<string | undefined> {\n try {\n const query = this.db('copilot_metrics')\n .where('type', type)\n .where('team_name', teamName ?? null)\n .orderBy('day', 'desc')\n .first('day');\n const res = await query;\n return res ? res.day : undefined;\n } catch (e) {\n return undefined;\n }\n }\n\n async getEarliestDayFromMetricsV2(\n type: MetricsType,\n teamName?: string,\n ): Promise<string | undefined> {\n try {\n const query = this.db('copilot_metrics')\n .where('type', type)\n .where('team_name', teamName ?? null)\n .orderBy('day', 'asc')\n .first('day');\n const res = await query;\n return res ? res.day : undefined;\n } catch (e) {\n return undefined;\n }\n }\n\n async getMetrics(\n startDate: string,\n endDate: string,\n type: MetricsType,\n teamName?: string,\n ): Promise<MetricDbRow[]> {\n if (teamName) {\n return await this.db<MetricDbRow>('metrics')\n .where('type', type)\n .where('team_name', teamName)\n .whereBetween('day', [startDate, endDate]);\n }\n return this.db<MetricDbRow>('metrics')\n .where('type', type)\n .whereNull('team_name')\n .whereBetween('day', [startDate, endDate]);\n }\n\n async getSeatMetrics(\n startDate: string,\n endDate: string,\n type: MetricsType,\n teamName?: string,\n ): Promise<SeatAnalysis[]> {\n return await this.db<SeatAnalysis>('seats')\n .where('type', type)\n .where('team_name', teamName ?? '')\n .whereBetween('day', [startDate, endDate])\n .orderBy('day', 'asc');\n }\n\n async getEngagementMetrics(\n startDate: string,\n endDate: string,\n type: MetricsType,\n teamName?: string,\n ): Promise<EngagementMetrics[]> {\n let query = this.db('copilot_metrics as cm')\n .select(\n 'cm.day',\n 'cm.type',\n 'cm.team_name',\n this.db.raw(\n 'CAST(MIN(cm.total_active_users) AS INTEGER) as total_active_users',\n ),\n this.db.raw(\n 'CAST(MIN(cm.total_engaged_users) AS INTEGER) as total_engaged_users',\n ),\n this.db.raw(\n 'CAST(MIN(ide_completions.total_engaged_users) AS INTEGER) as ide_completions_engaged_users',\n ),\n this.db.raw(\n 'CAST(MIN(ide_chats.total_engaged_users) AS INTEGER) as ide_chats_engaged_users',\n ),\n this.db.raw(\n 'CAST(MIN(dotcom_chats.total_engaged_users) AS INTEGER) as dotcom_chats_engaged_users',\n ),\n this.db.raw(\n 'CAST(MIN(dotcom_prs.total_engaged_users) AS INTEGER) as dotcom_prs_engaged_users',\n ),\n )\n .leftJoin('ide_completions', join => {\n join\n .on('ide_completions.day', '=', 'cm.day')\n .andOn('ide_completions.type', '=', 'cm.type');\n if (teamName) {\n join.andOn(\n 'ide_completions.team_name',\n '=',\n this.db.raw('?', [teamName]),\n );\n } else {\n join.andOnNull('ide_completions.team_name');\n }\n })\n .leftJoin('ide_chats', join => {\n join\n .on('ide_chats.day', '=', 'cm.day')\n .andOn('ide_chats.type', '=', 'cm.type');\n if (teamName) {\n join.andOn('ide_chats.team_name', '=', this.db.raw('?', [teamName]));\n } else {\n join.andOnNull('ide_chats.team_name');\n }\n })\n .leftJoin('dotcom_chats', join => {\n join\n .on('dotcom_chats.day', '=', 'cm.day')\n .andOn('dotcom_chats.type', '=', 'cm.type');\n if (teamName) {\n join.andOn(\n 'dotcom_chats.team_name',\n '=',\n this.db.raw('?', [teamName]),\n );\n } else {\n join.andOnNull('dotcom_chats.team_name');\n }\n })\n .leftJoin('dotcom_prs', join => {\n join\n .on('dotcom_prs.day', '=', 'cm.day')\n .andOn('dotcom_prs.type', '=', 'cm.type');\n if (teamName) {\n join.andOn('dotcom_prs.team_name', '=', this.db.raw('?', [teamName]));\n } else {\n join.andOnNull('dotcom_prs.team_name');\n }\n })\n .where('cm.type', type)\n .whereBetween('cm.day', [startDate, endDate])\n .groupBy('cm.day', 'cm.type', 'cm.team_name')\n .orderBy('cm.day', 'asc');\n\n if (teamName) {\n query = query.where('cm.team_name', teamName);\n } else {\n query = query.whereNull('cm.team_name');\n }\n\n return await query;\n }\n\n async getMetricsV2(\n startDate: string,\n endDate: string,\n type: MetricsType,\n teamName?: string,\n ): Promise<MetricDbRow[]> {\n let query = this.db('copilot_metrics as cm')\n .select(\n 'cm.day',\n 'cm.type',\n 'cm.team_name',\n this.db.raw(\n 'CAST(MIN(cm.total_active_users) AS INTEGER) as total_active_users',\n ),\n this.db.raw(\n 'CAST(MIN(ide_chats.total_engaged_users) AS INTEGER) as total_active_chat_users',\n ),\n this.db.raw(\n 'CAST(SUM(icelm.total_code_suggestions) AS INTEGER) as total_suggestions_count',\n ),\n this.db.raw(\n 'CAST(SUM(icelm.total_code_acceptances) AS INTEGER) as total_acceptances_count',\n ),\n this.db.raw(\n 'CAST(SUM(icelm.total_code_lines_suggested) AS INTEGER) as total_lines_suggested',\n ),\n this.db.raw(\n 'CAST(SUM(icelm.total_code_lines_accepted) AS INTEGER) as total_lines_accepted',\n ),\n this.db.raw(\n 'CAST(SUM(icem.total_chats) AS INTEGER) as total_chat_turns',\n ),\n this.db.raw(\n 'CAST(SUM(icem.total_chat_copy_events) AS INTEGER) as total_chat_acceptances',\n ),\n this.db.raw(\"'' as breakdown\"),\n )\n .join('ide_completions', join => {\n join\n .on('ide_completions.day', '=', 'cm.day')\n .andOn('ide_completions.type', '=', 'cm.type');\n if (teamName) {\n join.andOn(\n 'ide_completions.team_name',\n '=',\n this.db.raw('?', [teamName]),\n );\n } else {\n join.andOnNull('ide_completions.team_name');\n }\n })\n .join('ide_chats', join => {\n join\n .on('ide_chats.day', '=', 'cm.day')\n .andOn('ide_chats.type', '=', 'cm.type');\n if (teamName) {\n join.andOn('ide_chats.team_name', '=', this.db.raw('?', [teamName]));\n } else {\n join.andOnNull('ide_chats.team_name');\n }\n })\n .join(\n this.db.raw(\n `(SELECT day, type, team_name,\n SUM(total_code_suggestions) as total_code_suggestions, \n SUM(total_code_acceptances) as total_code_acceptances, \n SUM(total_code_lines_suggested) as total_code_lines_suggested, \n SUM(total_code_lines_accepted) as total_code_lines_accepted \n FROM ide_completions_language_editors_model_language GROUP BY day, type, team_name) \n as icelm`,\n ),\n join => {\n join\n .on('icelm.day', '=', 'cm.day')\n .andOn('icelm.type', '=', 'cm.type');\n if (teamName) {\n join.andOn('icelm.team_name', '=', this.db.raw('?', [teamName]));\n } else {\n join.andOnNull('icelm.team_name');\n }\n },\n )\n .join(\n this.db.raw(\n `(SELECT day, type, team_name, SUM(total_chats) as total_chats, \n SUM(total_chat_copy_events) as total_chat_copy_events \n FROM ide_chat_editors_model GROUP BY day, type, team_name) as icem`,\n ),\n join => {\n join.on('icem.day', '=', 'cm.day').andOn('icem.type', '=', 'cm.type');\n if (teamName) {\n join.andOn('icem.team_name', '=', this.db.raw('?', [teamName]));\n } else {\n join.andOnNull('icem.team_name');\n }\n },\n )\n .where('cm.type', type)\n .whereBetween('cm.day', [startDate, endDate])\n .groupBy('cm.day', 'cm.type', 'cm.team_name')\n .orderBy('cm.day', 'asc');\n\n if (teamName) {\n query = query.where('cm.team_name', teamName);\n } else {\n query = query.whereNull('cm.team_name');\n }\n\n return await query;\n }\n\n async getBreakdown(\n startDate: string,\n endDate: string,\n type: MetricsType,\n teamName?: string,\n ): Promise<Breakdown[]> {\n let query = this.db<Breakdown>('copilot_metrics as cm')\n .select(\n 'cm.day',\n 'icleml.editor as editor',\n 'icleml.language as language',\n this.db.raw(\n 'CAST(SUM(icleml.total_engaged_users) AS INTEGER) as active_users',\n ),\n this.db.raw(\n 'CAST(SUM(icleml.total_code_lines_suggested) AS INTEGER) as lines_suggested',\n ),\n this.db.raw(\n 'CAST(SUM(icleml.total_code_lines_accepted) AS INTEGER) as lines_accepted',\n ),\n this.db.raw(\n 'CAST(SUM(icleml.total_code_suggestions) AS INTEGER) as suggestions_count',\n ),\n this.db.raw(\n 'CAST(SUM(icleml.total_code_acceptances) AS INTEGER) as acceptances_count',\n ),\n )\n .join(\n 'ide_completions_language_editors_model_language as icleml',\n join => {\n join\n .on('icleml.day', '=', 'cm.day')\n .andOn('icleml.type', '=', 'cm.type');\n if (teamName) {\n join.andOn('icleml.team_name', '=', this.db.raw('?', [teamName]));\n } else {\n join.andOnNull('icleml.team_name');\n }\n },\n )\n .whereBetween('cm.day', [startDate, endDate])\n .where('icleml.model', 'default')\n .where('cm.type', type)\n .groupBy('cm.day', 'icleml.editor', 'icleml.language')\n .orderBy('cm.day', 'asc');\n\n if (teamName) {\n query = query.where('cm.team_name', teamName);\n } else {\n query = query.whereNull('cm.team_name');\n }\n\n return await query;\n }\n}\n"],"names":["resolvePackagePath"],"mappings":"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"}
@@ -88,6 +88,31 @@ async function createRouter(routerOptions, pluginOptions) {
88
88
  return res.json(metrics);
89
89
  }
90
90
  );
91
+ router.get(
92
+ "/engagements",
93
+ validateQuery.validateQuery(schema.metricsQuerySchema),
94
+ async (req, res) => {
95
+ const { startDate, endDate, type, team } = req.query;
96
+ const engagements = await db.getEngagementMetrics(
97
+ startDate,
98
+ endDate,
99
+ type,
100
+ team
101
+ );
102
+ if (!engagements) {
103
+ throw new errors.NotFoundError();
104
+ }
105
+ return res.json(engagements);
106
+ }
107
+ );
108
+ router.get("/seats", validateQuery.validateQuery(schema.metricsQuerySchema), async (req, res) => {
109
+ const { startDate, endDate, type, team } = req.query;
110
+ const seats = await db.getSeatMetrics(startDate, endDate, type, team);
111
+ if (!seats) {
112
+ throw new errors.NotFoundError();
113
+ }
114
+ return res.json(seats);
115
+ });
91
116
  router.get(
92
117
  "/metrics/period-range",
93
118
  validateQuery.validateQuery(schema.periodRangeQuerySchema),
@@ -1 +1 @@
1
- {"version":3,"file":"router.cjs.js","sources":["../../src/service/router.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport express from 'express';\nimport Router from 'express-promise-router';\nimport { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport {\n DatabaseService,\n LoggerService,\n readSchedulerServiceTaskScheduleDefinitionFromConfig,\n SchedulerService,\n SchedulerServiceTaskScheduleDefinition,\n} from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { NotFoundError } from '@backstage/errors';\nimport {\n Metric,\n PeriodRange,\n} from '@backstage-community/plugin-copilot-common';\nimport { DatabaseHandler } from '../db/DatabaseHandler';\nimport TaskManagement from '../task/TaskManagement';\nimport { GithubClient } from '../client/GithubClient';\nimport { validateQuery } from './validation/validateQuery';\nimport {\n MetricsQuery,\n metricsQuerySchema,\n PeriodRangeQuery,\n periodRangeQuerySchema,\n TeamQuery,\n teamQuerySchema,\n} from './validation/schema';\nimport { DateTime } from 'luxon';\n\n/**\n * Options for configuring the Copilot plugin.\n *\n * @public\n */\nexport interface PluginOptions {\n /**\n * Schedule configuration for the plugin.\n */\n schedule?: SchedulerServiceTaskScheduleDefinition;\n}\n\n/**\n * Options for configuring the router used by the Copilot plugin.\n *\n * @public\n */\nexport interface RouterOptions {\n /**\n * Logger service for the router.\n */\n logger: LoggerService;\n\n /**\n * Database service for the router.\n */\n database: DatabaseService;\n\n /**\n * Scheduler service for the router.\n */\n scheduler: SchedulerService;\n\n /**\n * Configuration for the router.\n */\n config: Config;\n}\n\nconst defaultSchedule: SchedulerServiceTaskScheduleDefinition = {\n frequency: { cron: '0 2 * * *' },\n timeout: { minutes: 15 },\n initialDelay: { minutes: 1 },\n scope: 'local',\n};\n\n/**\n * Creates an Express router configured based on the provided router options and plugin options.\n *\n * This function initializes the router with the appropriate middleware and routes based on the\n * configuration and options provided. It also schedules tasks if scheduling options are provided.\n *\n * @param routerOptions - Options for configuring the router, including services and configuration.\n * @returns A promise that resolves to an Express router instance.\n *\n * @public\n */\nexport async function createRouterFromConfig(routerOptions: RouterOptions) {\n const { config } = routerOptions;\n const pluginOptions: PluginOptions = {\n schedule: defaultSchedule,\n };\n if (config && config.has('copilot.schedule')) {\n pluginOptions.schedule =\n readSchedulerServiceTaskScheduleDefinitionFromConfig(\n config.getConfig('copilot.schedule'),\n );\n }\n return createRouter(routerOptions, pluginOptions);\n}\n\n/** @private */\nasync function createRouter(\n routerOptions: RouterOptions,\n pluginOptions: PluginOptions,\n): Promise<express.Router> {\n const { logger, database, scheduler, config } = routerOptions;\n const { schedule } = pluginOptions;\n\n const db = await DatabaseHandler.create({ database });\n const api = await GithubClient.fromConfig(config);\n\n await scheduler.scheduleTask({\n id: 'copilot-metrics',\n ...(schedule ?? defaultSchedule),\n fn: async () =>\n await TaskManagement.create({ db, logger, api, config }).runAsync(),\n });\n\n const router = Router();\n router.use(express.json());\n\n router.get('/health', (_, response) => {\n logger.info('PONG!');\n response.json({ status: 'ok' });\n });\n\n router.get(\n '/metrics',\n validateQuery(metricsQuerySchema),\n async (req, res) => {\n const { startDate, endDate, type, team } = req.query as MetricsQuery;\n let metrics: Metric[] = [];\n\n // if startDate is earlier than last date of MetricsV1, fetch the old data\n const lastDayOfOldMetrics = await db.getMostRecentDayFromMetrics(\n type,\n team,\n );\n\n if (\n startDate &&\n lastDayOfOldMetrics &&\n DateTime.fromISO(startDate) <=\n DateTime.fromJSDate(new Date(lastDayOfOldMetrics))\n ) {\n const result = await db.getMetrics(startDate, endDate, type, team);\n metrics = result.map(metric => ({\n ...metric,\n breakdown: JSON.parse(metric.breakdown),\n }));\n }\n\n // if endDate is later or equal to first day of new metrics, fetch those also and merge into metrics\n const firstDayOfNewMetrics = await db.getEarliestDayFromMetricsV2(\n type,\n team,\n );\n if (\n endDate &&\n firstDayOfNewMetrics &&\n DateTime.fromISO(endDate) >=\n DateTime.fromJSDate(new Date(firstDayOfNewMetrics))\n ) {\n const result = await db.getMetricsV2(startDate, endDate, type, team);\n const breakdown = await db.getBreakdown(startDate, endDate, type, team);\n\n const newMetrics = result.map(metric => ({\n ...metric,\n breakdown: breakdown.filter(day => {\n const metricDate = DateTime.fromJSDate(new Date(metric.day));\n const dayDate = DateTime.fromJSDate(new Date(day.day));\n return metricDate.equals(dayDate);\n }),\n }));\n\n // Merge new metrics with old metrics\n metrics = [...metrics, ...newMetrics];\n }\n\n return res.json(metrics);\n },\n );\n\n router.get(\n '/metrics/period-range',\n validateQuery(periodRangeQuerySchema),\n async (req, res) => {\n const { type } = req.query as PeriodRangeQuery;\n const oldMetricRange = await db.getPeriodRange(type);\n const newMetricRange = await db.getPeriodRangeV2(type);\n\n if (!oldMetricRange && !newMetricRange) {\n throw new NotFoundError();\n }\n\n // Determine the minDate, prioritizing oldMetricRange if available\n const minDate = oldMetricRange?.minDate || newMetricRange?.minDate;\n\n // Determine the maxDate, prioritizing newMetricRange if available\n const maxDate = newMetricRange?.maxDate || oldMetricRange?.maxDate;\n\n // Make sure both minDate and maxDate are defined\n if (!minDate || !maxDate) {\n throw new NotFoundError('Unable to determine metric date range');\n }\n\n const result: PeriodRange = { minDate, maxDate };\n\n return res.json(result);\n },\n );\n\n router.get('/teams', validateQuery(teamQuerySchema), async (req, res) => {\n const { type, startDate, endDate } = req.query as TeamQuery;\n\n const result = await db.getTeams(type, startDate, endDate);\n\n if (!result) {\n throw new NotFoundError();\n }\n\n return res.json(result);\n });\n\n router.use(MiddlewareFactory.create({ config, logger }).error);\n return router;\n}\n"],"names":["readSchedulerServiceTaskScheduleDefinitionFromConfig","DatabaseHandler","GithubClient","TaskManagement","Router","express","validateQuery","metricsQuerySchema","DateTime","periodRangeQuerySchema","NotFoundError","teamQuerySchema","MiddlewareFactory"],"mappings":"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"}
1
+ {"version":3,"file":"router.cjs.js","sources":["../../src/service/router.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport express from 'express';\nimport Router from 'express-promise-router';\nimport { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport {\n DatabaseService,\n LoggerService,\n readSchedulerServiceTaskScheduleDefinitionFromConfig,\n SchedulerService,\n SchedulerServiceTaskScheduleDefinition,\n} from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { NotFoundError } from '@backstage/errors';\nimport {\n Metric,\n PeriodRange,\n} from '@backstage-community/plugin-copilot-common';\nimport { DatabaseHandler } from '../db/DatabaseHandler';\nimport TaskManagement from '../task/TaskManagement';\nimport { GithubClient } from '../client/GithubClient';\nimport { validateQuery } from './validation/validateQuery';\nimport {\n MetricsQuery,\n metricsQuerySchema,\n PeriodRangeQuery,\n periodRangeQuerySchema,\n TeamQuery,\n teamQuerySchema,\n} from './validation/schema';\nimport { DateTime } from 'luxon';\n\n/**\n * Options for configuring the Copilot plugin.\n *\n * @public\n */\nexport interface PluginOptions {\n /**\n * Schedule configuration for the plugin.\n */\n schedule?: SchedulerServiceTaskScheduleDefinition;\n}\n\n/**\n * Options for configuring the router used by the Copilot plugin.\n *\n * @public\n */\nexport interface RouterOptions {\n /**\n * Logger service for the router.\n */\n logger: LoggerService;\n\n /**\n * Database service for the router.\n */\n database: DatabaseService;\n\n /**\n * Scheduler service for the router.\n */\n scheduler: SchedulerService;\n\n /**\n * Configuration for the router.\n */\n config: Config;\n}\n\nconst defaultSchedule: SchedulerServiceTaskScheduleDefinition = {\n frequency: { cron: '0 2 * * *' },\n timeout: { minutes: 15 },\n initialDelay: { minutes: 1 },\n scope: 'local',\n};\n\n/**\n * Creates an Express router configured based on the provided router options and plugin options.\n *\n * This function initializes the router with the appropriate middleware and routes based on the\n * configuration and options provided. It also schedules tasks if scheduling options are provided.\n *\n * @param routerOptions - Options for configuring the router, including services and configuration.\n * @returns A promise that resolves to an Express router instance.\n *\n * @public\n */\nexport async function createRouterFromConfig(routerOptions: RouterOptions) {\n const { config } = routerOptions;\n const pluginOptions: PluginOptions = {\n schedule: defaultSchedule,\n };\n if (config && config.has('copilot.schedule')) {\n pluginOptions.schedule =\n readSchedulerServiceTaskScheduleDefinitionFromConfig(\n config.getConfig('copilot.schedule'),\n );\n }\n return createRouter(routerOptions, pluginOptions);\n}\n\n/** @private */\nasync function createRouter(\n routerOptions: RouterOptions,\n pluginOptions: PluginOptions,\n): Promise<express.Router> {\n const { logger, database, scheduler, config } = routerOptions;\n const { schedule } = pluginOptions;\n\n const db = await DatabaseHandler.create({ database });\n const api = await GithubClient.fromConfig(config);\n\n await scheduler.scheduleTask({\n id: 'copilot-metrics',\n ...(schedule ?? defaultSchedule),\n fn: async () =>\n await TaskManagement.create({ db, logger, api, config }).runAsync(),\n });\n\n const router = Router();\n router.use(express.json());\n\n router.get('/health', (_, response) => {\n logger.info('PONG!');\n response.json({ status: 'ok' });\n });\n\n router.get(\n '/metrics',\n validateQuery(metricsQuerySchema),\n async (req, res) => {\n const { startDate, endDate, type, team } = req.query as MetricsQuery;\n let metrics: Metric[] = [];\n\n // if startDate is earlier than last date of MetricsV1, fetch the old data\n const lastDayOfOldMetrics = await db.getMostRecentDayFromMetrics(\n type,\n team,\n );\n\n if (\n startDate &&\n lastDayOfOldMetrics &&\n DateTime.fromISO(startDate) <=\n DateTime.fromJSDate(new Date(lastDayOfOldMetrics))\n ) {\n const result = await db.getMetrics(startDate, endDate, type, team);\n metrics = result.map(metric => ({\n ...metric,\n breakdown: JSON.parse(metric.breakdown),\n }));\n }\n\n // if endDate is later or equal to first day of new metrics, fetch those also and merge into metrics\n const firstDayOfNewMetrics = await db.getEarliestDayFromMetricsV2(\n type,\n team,\n );\n if (\n endDate &&\n firstDayOfNewMetrics &&\n DateTime.fromISO(endDate) >=\n DateTime.fromJSDate(new Date(firstDayOfNewMetrics))\n ) {\n const result = await db.getMetricsV2(startDate, endDate, type, team);\n const breakdown = await db.getBreakdown(startDate, endDate, type, team);\n\n const newMetrics = result.map(metric => ({\n ...metric,\n breakdown: breakdown.filter(day => {\n const metricDate = DateTime.fromJSDate(new Date(metric.day));\n const dayDate = DateTime.fromJSDate(new Date(day.day));\n return metricDate.equals(dayDate);\n }),\n }));\n\n // Merge new metrics with old metrics\n metrics = [...metrics, ...newMetrics];\n }\n\n return res.json(metrics);\n },\n );\n\n router.get(\n '/engagements',\n validateQuery(metricsQuerySchema),\n async (req, res) => {\n const { startDate, endDate, type, team } = req.query as MetricsQuery;\n\n const engagements = await db.getEngagementMetrics(\n startDate,\n endDate,\n type,\n team,\n );\n if (!engagements) {\n throw new NotFoundError();\n }\n\n return res.json(engagements);\n },\n );\n\n router.get('/seats', validateQuery(metricsQuerySchema), async (req, res) => {\n const { startDate, endDate, type, team } = req.query as MetricsQuery;\n\n const seats = await db.getSeatMetrics(startDate, endDate, type, team);\n if (!seats) {\n throw new NotFoundError();\n }\n\n return res.json(seats);\n });\n\n router.get(\n '/metrics/period-range',\n validateQuery(periodRangeQuerySchema),\n async (req, res) => {\n const { type } = req.query as PeriodRangeQuery;\n const oldMetricRange = await db.getPeriodRange(type);\n const newMetricRange = await db.getPeriodRangeV2(type);\n\n if (!oldMetricRange && !newMetricRange) {\n throw new NotFoundError();\n }\n\n // Determine the minDate, prioritizing oldMetricRange if available\n const minDate = oldMetricRange?.minDate || newMetricRange?.minDate;\n\n // Determine the maxDate, prioritizing newMetricRange if available\n const maxDate = newMetricRange?.maxDate || oldMetricRange?.maxDate;\n\n // Make sure both minDate and maxDate are defined\n if (!minDate || !maxDate) {\n throw new NotFoundError('Unable to determine metric date range');\n }\n\n const result: PeriodRange = { minDate, maxDate };\n\n return res.json(result);\n },\n );\n\n router.get('/teams', validateQuery(teamQuerySchema), async (req, res) => {\n const { type, startDate, endDate } = req.query as TeamQuery;\n\n const result = await db.getTeams(type, startDate, endDate);\n\n if (!result) {\n throw new NotFoundError();\n }\n\n return res.json(result);\n });\n\n router.use(MiddlewareFactory.create({ config, logger }).error);\n return router;\n}\n"],"names":["readSchedulerServiceTaskScheduleDefinitionFromConfig","DatabaseHandler","GithubClient","TaskManagement","Router","express","validateQuery","metricsQuerySchema","DateTime","NotFoundError","periodRangeQuerySchema","teamQuerySchema","MiddlewareFactory"],"mappings":"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"}
@@ -92,6 +92,9 @@ async function discoverEnterpriseMetrics({
92
92
  await batchInsert.batchInsertInChunks(ideChatEditorModels, 30, async (chunk) => {
93
93
  await db.batchInsertIdeChatEditorModels(chunk);
94
94
  });
95
+ const seats = await api.fetchOrganizationSeats();
96
+ const seatsToInsert = metricHelpers.convertToSeatAnalysis(seats, type);
97
+ await db.insertSeatAnalysys(seatsToInsert);
95
98
  logger.info(
96
99
  "[discoverEnterpriseMetrics] Inserted new metrics into the database"
97
100
  );
@@ -1 +1 @@
1
- {"version":3,"file":"EnterpriseTask.cjs.js","sources":["../../src/task/EnterpriseTask.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n CopilotMetrics,\n MetricsType,\n} from '@backstage-community/plugin-copilot-common';\nimport { batchInsertInChunks } from '../utils/batchInsert';\nimport {\n filterBaseMetrics,\n filterIdeChatEditorModelMetrics,\n filterIdeChatMetrics,\n filterIdeCompletionEditorMetrics,\n filterIdeCompletionEditorModelLanguageMetrics,\n filterIdeCompletionEditorModelMetrics,\n filterIdeCompletionLanguageMetrics,\n filterIdeCompletionMetrics,\n filterIdeEditorMetrics,\n filterNewMetricsV2,\n} from '../utils/metricHelpers';\nimport { TaskOptions } from './TaskManagement';\n\nexport async function discoverEnterpriseMetrics({\n api,\n logger,\n db,\n config,\n}: TaskOptions): Promise<void> {\n if (!config.getOptionalString('copilot.enterprise')) {\n logger.info(\n '[discoverEnterpriseMetrics] Skipping: Enterprise configuration not found.',\n );\n return;\n }\n\n const type: MetricsType = 'enterprise';\n\n try {\n const copilotMetrics = await api.fetchEnterpriseCopilotMetrics();\n logger.info(\n `[discoverEnterpriseMetrics] Fetched ${copilotMetrics.length} metrics`,\n );\n\n const lastDay = await db.getMostRecentDayFromMetricsV2(type);\n logger.info(`[discoverEnterpriseMetrics] Found last day: ${lastDay}`);\n\n const newMetrics: CopilotMetrics[] = filterNewMetricsV2(\n copilotMetrics,\n lastDay,\n );\n logger.info(\n `[discoverEnterpriseMetrics] Found ${newMetrics.length} new metrics to insert`,\n );\n\n if (newMetrics.length > 0) {\n const coPilotMetrics = filterBaseMetrics(newMetrics, type);\n const ideCompletionsToInsert = filterIdeCompletionMetrics(\n newMetrics,\n type,\n );\n const ideCompletionsLanguagesToInsert =\n filterIdeCompletionLanguageMetrics(newMetrics, type);\n const ideCompletionsEditorsToInsert = filterIdeCompletionEditorMetrics(\n newMetrics,\n type,\n );\n const ideCompletionsEditorModelsToInsert =\n filterIdeCompletionEditorModelMetrics(newMetrics, type);\n const ideCompletionsEditorModelLanguagesToInsert =\n filterIdeCompletionEditorModelLanguageMetrics(newMetrics, type);\n const ideChats = filterIdeChatMetrics(newMetrics, type);\n const ideChatEditors = filterIdeEditorMetrics(newMetrics, type);\n const ideChatEditorModels = filterIdeChatEditorModelMetrics(\n newMetrics,\n type,\n );\n\n await batchInsertInChunks(coPilotMetrics, 30, async chunk => {\n await db.batchInsertMetrics(chunk);\n });\n\n await batchInsertInChunks(ideCompletionsToInsert, 30, async chunk => {\n await db.batchInsertIdeCompletions(chunk);\n });\n\n await batchInsertInChunks(\n ideCompletionsLanguagesToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsLanguages(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorsToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditors(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorModelsToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditorModels(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorModelLanguagesToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditorModelLanguages(chunk);\n },\n );\n\n await batchInsertInChunks(ideChats, 30, async chunk => {\n await db.batchInsertIdeChats(chunk);\n });\n\n await batchInsertInChunks(ideChatEditors, 30, async chunk => {\n await db.batchInsertIdeChatEditors(chunk);\n });\n\n await batchInsertInChunks(ideChatEditorModels, 30, async chunk => {\n await db.batchInsertIdeChatEditorModels(chunk);\n });\n\n logger.info(\n '[discoverEnterpriseMetrics] Inserted new metrics into the database',\n );\n } else {\n logger.info('[discoverEnterpriseMetrics] No new metrics found to insert');\n }\n } catch (error) {\n logger.error(\n `[discoverEnterpriseMetrics] An error occurred while processing Github Copilot metrics: ${error}`,\n );\n throw error;\n }\n}\n"],"names":["filterNewMetricsV2","filterBaseMetrics","filterIdeCompletionMetrics","filterIdeCompletionLanguageMetrics","filterIdeCompletionEditorMetrics","filterIdeCompletionEditorModelMetrics","filterIdeCompletionEditorModelLanguageMetrics","filterIdeChatMetrics","filterIdeEditorMetrics","filterIdeChatEditorModelMetrics","batchInsertInChunks"],"mappings":"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"}
1
+ {"version":3,"file":"EnterpriseTask.cjs.js","sources":["../../src/task/EnterpriseTask.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n CopilotMetrics,\n MetricsType,\n} from '@backstage-community/plugin-copilot-common';\nimport { batchInsertInChunks } from '../utils/batchInsert';\nimport {\n convertToSeatAnalysis,\n filterBaseMetrics,\n filterIdeChatEditorModelMetrics,\n filterIdeChatMetrics,\n filterIdeCompletionEditorMetrics,\n filterIdeCompletionEditorModelLanguageMetrics,\n filterIdeCompletionEditorModelMetrics,\n filterIdeCompletionLanguageMetrics,\n filterIdeCompletionMetrics,\n filterIdeEditorMetrics,\n filterNewMetricsV2,\n} from '../utils/metricHelpers';\nimport { TaskOptions } from './TaskManagement';\n\nexport async function discoverEnterpriseMetrics({\n api,\n logger,\n db,\n config,\n}: TaskOptions): Promise<void> {\n if (!config.getOptionalString('copilot.enterprise')) {\n logger.info(\n '[discoverEnterpriseMetrics] Skipping: Enterprise configuration not found.',\n );\n return;\n }\n\n const type: MetricsType = 'enterprise';\n\n try {\n const copilotMetrics = await api.fetchEnterpriseCopilotMetrics();\n logger.info(\n `[discoverEnterpriseMetrics] Fetched ${copilotMetrics.length} metrics`,\n );\n\n const lastDay = await db.getMostRecentDayFromMetricsV2(type);\n logger.info(`[discoverEnterpriseMetrics] Found last day: ${lastDay}`);\n\n const newMetrics: CopilotMetrics[] = filterNewMetricsV2(\n copilotMetrics,\n lastDay,\n );\n logger.info(\n `[discoverEnterpriseMetrics] Found ${newMetrics.length} new metrics to insert`,\n );\n\n if (newMetrics.length > 0) {\n const coPilotMetrics = filterBaseMetrics(newMetrics, type);\n const ideCompletionsToInsert = filterIdeCompletionMetrics(\n newMetrics,\n type,\n );\n const ideCompletionsLanguagesToInsert =\n filterIdeCompletionLanguageMetrics(newMetrics, type);\n const ideCompletionsEditorsToInsert = filterIdeCompletionEditorMetrics(\n newMetrics,\n type,\n );\n const ideCompletionsEditorModelsToInsert =\n filterIdeCompletionEditorModelMetrics(newMetrics, type);\n const ideCompletionsEditorModelLanguagesToInsert =\n filterIdeCompletionEditorModelLanguageMetrics(newMetrics, type);\n const ideChats = filterIdeChatMetrics(newMetrics, type);\n const ideChatEditors = filterIdeEditorMetrics(newMetrics, type);\n const ideChatEditorModels = filterIdeChatEditorModelMetrics(\n newMetrics,\n type,\n );\n\n await batchInsertInChunks(coPilotMetrics, 30, async chunk => {\n await db.batchInsertMetrics(chunk);\n });\n\n await batchInsertInChunks(ideCompletionsToInsert, 30, async chunk => {\n await db.batchInsertIdeCompletions(chunk);\n });\n\n await batchInsertInChunks(\n ideCompletionsLanguagesToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsLanguages(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorsToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditors(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorModelsToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditorModels(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorModelLanguagesToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditorModelLanguages(chunk);\n },\n );\n\n await batchInsertInChunks(ideChats, 30, async chunk => {\n await db.batchInsertIdeChats(chunk);\n });\n\n await batchInsertInChunks(ideChatEditors, 30, async chunk => {\n await db.batchInsertIdeChatEditors(chunk);\n });\n\n await batchInsertInChunks(ideChatEditorModels, 30, async chunk => {\n await db.batchInsertIdeChatEditorModels(chunk);\n });\n\n const seats = await api.fetchOrganizationSeats();\n const seatsToInsert = convertToSeatAnalysis(seats, type);\n await db.insertSeatAnalysys(seatsToInsert);\n\n logger.info(\n '[discoverEnterpriseMetrics] Inserted new metrics into the database',\n );\n } else {\n logger.info('[discoverEnterpriseMetrics] No new metrics found to insert');\n }\n } catch (error) {\n logger.error(\n `[discoverEnterpriseMetrics] An error occurred while processing Github Copilot metrics: ${error}`,\n );\n throw error;\n }\n}\n"],"names":["filterNewMetricsV2","filterBaseMetrics","filterIdeCompletionMetrics","filterIdeCompletionLanguageMetrics","filterIdeCompletionEditorMetrics","filterIdeCompletionEditorModelMetrics","filterIdeCompletionEditorModelLanguageMetrics","filterIdeChatMetrics","filterIdeEditorMetrics","filterIdeChatEditorModelMetrics","batchInsertInChunks","convertToSeatAnalysis"],"mappings":"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"}
@@ -109,6 +109,13 @@ async function discoverEnterpriseTeamMetrics({
109
109
  await batchInsert.batchInsertInChunks(ideChatEditorModels, 30, async (chunk) => {
110
110
  await db.batchInsertIdeChatEditorModels(chunk);
111
111
  });
112
+ const seats = await api.fetchEnterpriseSeats();
113
+ const seatsToInsert = metricHelpers.convertToTeamSeatAnalysis(
114
+ seats,
115
+ type,
116
+ team.slug
117
+ );
118
+ await db.insertSeatAnalysys(seatsToInsert);
112
119
  logger.info(
113
120
  `[discoverEnterpriseTeamMetrics] Inserted new metrics into the database for team: ${team.slug}`
114
121
  );
@@ -1 +1 @@
1
- {"version":3,"file":"EnterpriseTeamTask.cjs.js","sources":["../../src/task/EnterpriseTeamTask.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n CopilotMetrics,\n MetricsType,\n} from '@backstage-community/plugin-copilot-common';\nimport { batchInsertInChunks } from '../utils/batchInsert';\nimport {\n filterBaseMetrics,\n filterIdeChatEditorModelMetrics,\n filterIdeChatMetrics,\n filterIdeCompletionEditorMetrics,\n filterIdeCompletionEditorModelLanguageMetrics,\n filterIdeCompletionEditorModelMetrics,\n filterIdeCompletionLanguageMetrics,\n filterIdeCompletionMetrics,\n filterIdeEditorMetrics,\n filterNewMetricsV2,\n} from '../utils/metricHelpers';\nimport { TaskOptions } from './TaskManagement';\n\nexport async function discoverEnterpriseTeamMetrics({\n api,\n logger,\n db,\n config,\n}: TaskOptions): Promise<void> {\n if (!config.getOptionalString('copilot.enterprise')) {\n logger.info(\n '[discoverEnterpriseTeamMetrics] Skipping: Enterprise configuration not found.',\n );\n return;\n }\n\n const type: MetricsType = 'enterprise';\n\n try {\n const teams = await api.fetchEnterpriseTeams();\n logger.info(\n `[discoverEnterpriseTeamMetrics] Fetched ${teams.length} teams`,\n );\n\n for (const team of teams) {\n try {\n logger.info(\n `[discoverEnterpriseTeamMetrics] Fetching metrics for team: ${team.slug}`,\n );\n\n const copilotMetrics = await api.fetchEnterpriseTeamCopilotMetrics(\n team.slug,\n );\n\n const lastDay = await db.getMostRecentDayFromMetricsV2(type, team.slug);\n logger.info(\n `[discoverEnterpriseTeamMetrics] Found last day: ${lastDay}`,\n );\n\n const newMetrics: CopilotMetrics[] = filterNewMetricsV2(\n copilotMetrics,\n lastDay,\n );\n logger.info(\n `[discoverEnterpriseTeamMetrics] Found ${newMetrics.length} new metrics to insert for team: ${team.slug}`,\n );\n\n if (newMetrics.length > 0) {\n const coPilotMetrics = filterBaseMetrics(newMetrics, type, team.slug);\n const ideCompletionsToInsert = filterIdeCompletionMetrics(\n newMetrics,\n type,\n team.slug,\n );\n const ideCompletionsLanguagesToInsert =\n filterIdeCompletionLanguageMetrics(newMetrics, type, team.slug);\n const ideCompletionsEditorsToInsert =\n filterIdeCompletionEditorMetrics(newMetrics, type, team.slug);\n const ideCompletionsEditorModelsToInsert =\n filterIdeCompletionEditorModelMetrics(newMetrics, type, team.slug);\n const ideCompletionsEditorModelLanguagesToInsert =\n filterIdeCompletionEditorModelLanguageMetrics(\n newMetrics,\n type,\n team.slug,\n );\n const ideChats = filterIdeChatMetrics(newMetrics, type, team.slug);\n const ideChatEditors = filterIdeEditorMetrics(\n newMetrics,\n type,\n team.slug,\n );\n const ideChatEditorModels = filterIdeChatEditorModelMetrics(\n newMetrics,\n type,\n team.slug,\n );\n\n await batchInsertInChunks(coPilotMetrics, 30, async chunk => {\n await db.batchInsertMetrics(chunk);\n });\n\n await batchInsertInChunks(ideCompletionsToInsert, 30, async chunk => {\n await db.batchInsertIdeCompletions(chunk);\n });\n\n await batchInsertInChunks(\n ideCompletionsLanguagesToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsLanguages(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorsToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditors(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorModelsToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditorModels(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorModelLanguagesToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditorModelLanguages(chunk);\n },\n );\n\n await batchInsertInChunks(ideChats, 30, async chunk => {\n await db.batchInsertIdeChats(chunk);\n });\n\n await batchInsertInChunks(ideChatEditors, 30, async chunk => {\n await db.batchInsertIdeChatEditors(chunk);\n });\n\n await batchInsertInChunks(ideChatEditorModels, 30, async chunk => {\n await db.batchInsertIdeChatEditorModels(chunk);\n });\n\n logger.info(\n `[discoverEnterpriseTeamMetrics] Inserted new metrics into the database for team: ${team.slug}`,\n );\n } else {\n logger.info(\n `[discoverEnterpriseTeamMetrics] No new metrics found to insert for team: ${team.slug}`,\n );\n }\n } catch (error) {\n logger.error(\n `[discoverEnterpriseTeamMetrics] Error processing metrics for team ${team.slug}: ${error}`,\n );\n }\n }\n } catch (error) {\n logger.error(\n `[discoverEnterpriseTeamMetrics] Error fetching teams: ${error}`,\n );\n throw error;\n }\n}\n"],"names":["filterNewMetricsV2","filterBaseMetrics","filterIdeCompletionMetrics","filterIdeCompletionLanguageMetrics","filterIdeCompletionEditorMetrics","filterIdeCompletionEditorModelMetrics","filterIdeCompletionEditorModelLanguageMetrics","filterIdeChatMetrics","filterIdeEditorMetrics","filterIdeChatEditorModelMetrics","batchInsertInChunks"],"mappings":";;;;;AAkCA,eAAsB,6BAA8B,CAAA;AAAA,EAClD,GAAA;AAAA,EACA,MAAA;AAAA,EACA,EAAA;AAAA,EACA;AACF,CAA+B,EAAA;AAC7B,EAAA,IAAI,CAAC,MAAA,CAAO,iBAAkB,CAAA,oBAAoB,CAAG,EAAA;AACnD,IAAO,MAAA,CAAA,IAAA;AAAA,MACL;AAAA,KACF;AACA,IAAA;AAAA;AAGF,EAAA,MAAM,IAAoB,GAAA,YAAA;AAE1B,EAAI,IAAA;AACF,IAAM,MAAA,KAAA,GAAQ,MAAM,GAAA,CAAI,oBAAqB,EAAA;AAC7C,IAAO,MAAA,CAAA,IAAA;AAAA,MACL,CAAA,wCAAA,EAA2C,MAAM,MAAM,CAAA,MAAA;AAAA,KACzD;AAEA,IAAA,KAAA,MAAW,QAAQ,KAAO,EAAA;AACxB,MAAI,IAAA;AACF,QAAO,MAAA,CAAA,IAAA;AAAA,UACL,CAAA,2DAAA,EAA8D,KAAK,IAAI,CAAA;AAAA,SACzE;AAEA,QAAM,MAAA,cAAA,GAAiB,MAAM,GAAI,CAAA,iCAAA;AAAA,UAC/B,IAAK,CAAA;AAAA,SACP;AAEA,QAAA,MAAM,UAAU,MAAM,EAAA,CAAG,6BAA8B,CAAA,IAAA,EAAM,KAAK,IAAI,CAAA;AACtE,QAAO,MAAA,CAAA,IAAA;AAAA,UACL,mDAAmD,OAAO,CAAA;AAAA,SAC5D;AAEA,QAAA,MAAM,UAA+B,GAAAA,gCAAA;AAAA,UACnC,cAAA;AAAA,UACA;AAAA,SACF;AACA,QAAO,MAAA,CAAA,IAAA;AAAA,UACL,CAAyC,sCAAA,EAAA,UAAA,CAAW,MAAM,CAAA,iCAAA,EAAoC,KAAK,IAAI,CAAA;AAAA,SACzG;AAEA,QAAI,IAAA,UAAA,CAAW,SAAS,CAAG,EAAA;AACzB,UAAA,MAAM,cAAiB,GAAAC,+BAAA,CAAkB,UAAY,EAAA,IAAA,EAAM,KAAK,IAAI,CAAA;AACpE,UAAA,MAAM,sBAAyB,GAAAC,wCAAA;AAAA,YAC7B,UAAA;AAAA,YACA,IAAA;AAAA,YACA,IAAK,CAAA;AAAA,WACP;AACA,UAAA,MAAM,+BACJ,GAAAC,gDAAA,CAAmC,UAAY,EAAA,IAAA,EAAM,KAAK,IAAI,CAAA;AAChE,UAAA,MAAM,6BACJ,GAAAC,8CAAA,CAAiC,UAAY,EAAA,IAAA,EAAM,KAAK,IAAI,CAAA;AAC9D,UAAA,MAAM,kCACJ,GAAAC,mDAAA,CAAsC,UAAY,EAAA,IAAA,EAAM,KAAK,IAAI,CAAA;AACnE,UAAA,MAAM,0CACJ,GAAAC,2DAAA;AAAA,YACE,UAAA;AAAA,YACA,IAAA;AAAA,YACA,IAAK,CAAA;AAAA,WACP;AACF,UAAA,MAAM,QAAW,GAAAC,kCAAA,CAAqB,UAAY,EAAA,IAAA,EAAM,KAAK,IAAI,CAAA;AACjE,UAAA,MAAM,cAAiB,GAAAC,oCAAA;AAAA,YACrB,UAAA;AAAA,YACA,IAAA;AAAA,YACA,IAAK,CAAA;AAAA,WACP;AACA,UAAA,MAAM,mBAAsB,GAAAC,6CAAA;AAAA,YAC1B,UAAA;AAAA,YACA,IAAA;AAAA,YACA,IAAK,CAAA;AAAA,WACP;AAEA,UAAA,MAAMC,+BAAoB,CAAA,cAAA,EAAgB,EAAI,EAAA,OAAM,KAAS,KAAA;AAC3D,YAAM,MAAA,EAAA,CAAG,mBAAmB,KAAK,CAAA;AAAA,WAClC,CAAA;AAED,UAAA,MAAMA,+BAAoB,CAAA,sBAAA,EAAwB,EAAI,EAAA,OAAM,KAAS,KAAA;AACnE,YAAM,MAAA,EAAA,CAAG,0BAA0B,KAAK,CAAA;AAAA,WACzC,CAAA;AAED,UAAM,MAAAA,+BAAA;AAAA,YACJ,+BAAA;AAAA,YACA,EAAA;AAAA,YACA,OAAM,KAAS,KAAA;AACb,cAAM,MAAA,EAAA,CAAG,mCAAmC,KAAK,CAAA;AAAA;AACnD,WACF;AAEA,UAAM,MAAAA,+BAAA;AAAA,YACJ,6BAAA;AAAA,YACA,EAAA;AAAA,YACA,OAAM,KAAS,KAAA;AACb,cAAM,MAAA,EAAA,CAAG,iCAAiC,KAAK,CAAA;AAAA;AACjD,WACF;AAEA,UAAM,MAAAA,+BAAA;AAAA,YACJ,kCAAA;AAAA,YACA,EAAA;AAAA,YACA,OAAM,KAAS,KAAA;AACb,cAAM,MAAA,EAAA,CAAG,sCAAsC,KAAK,CAAA;AAAA;AACtD,WACF;AAEA,UAAM,MAAAA,+BAAA;AAAA,YACJ,0CAAA;AAAA,YACA,EAAA;AAAA,YACA,OAAM,KAAS,KAAA;AACb,cAAM,MAAA,EAAA,CAAG,8CAA8C,KAAK,CAAA;AAAA;AAC9D,WACF;AAEA,UAAA,MAAMA,+BAAoB,CAAA,QAAA,EAAU,EAAI,EAAA,OAAM,KAAS,KAAA;AACrD,YAAM,MAAA,EAAA,CAAG,oBAAoB,KAAK,CAAA;AAAA,WACnC,CAAA;AAED,UAAA,MAAMA,+BAAoB,CAAA,cAAA,EAAgB,EAAI,EAAA,OAAM,KAAS,KAAA;AAC3D,YAAM,MAAA,EAAA,CAAG,0BAA0B,KAAK,CAAA;AAAA,WACzC,CAAA;AAED,UAAA,MAAMA,+BAAoB,CAAA,mBAAA,EAAqB,EAAI,EAAA,OAAM,KAAS,KAAA;AAChE,YAAM,MAAA,EAAA,CAAG,+BAA+B,KAAK,CAAA;AAAA,WAC9C,CAAA;AAED,UAAO,MAAA,CAAA,IAAA;AAAA,YACL,CAAA,iFAAA,EAAoF,KAAK,IAAI,CAAA;AAAA,WAC/F;AAAA,SACK,MAAA;AACL,UAAO,MAAA,CAAA,IAAA;AAAA,YACL,CAAA,yEAAA,EAA4E,KAAK,IAAI,CAAA;AAAA,WACvF;AAAA;AACF,eACO,KAAO,EAAA;AACd,QAAO,MAAA,CAAA,KAAA;AAAA,UACL,CAAqE,kEAAA,EAAA,IAAA,CAAK,IAAI,CAAA,EAAA,EAAK,KAAK,CAAA;AAAA,SAC1F;AAAA;AACF;AACF,WACO,KAAO,EAAA;AACd,IAAO,MAAA,CAAA,KAAA;AAAA,MACL,yDAAyD,KAAK,CAAA;AAAA,KAChE;AACA,IAAM,MAAA,KAAA;AAAA;AAEV;;;;"}
1
+ {"version":3,"file":"EnterpriseTeamTask.cjs.js","sources":["../../src/task/EnterpriseTeamTask.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n CopilotMetrics,\n MetricsType,\n} from '@backstage-community/plugin-copilot-common';\nimport { batchInsertInChunks } from '../utils/batchInsert';\nimport {\n convertToTeamSeatAnalysis,\n filterBaseMetrics,\n filterIdeChatEditorModelMetrics,\n filterIdeChatMetrics,\n filterIdeCompletionEditorMetrics,\n filterIdeCompletionEditorModelLanguageMetrics,\n filterIdeCompletionEditorModelMetrics,\n filterIdeCompletionLanguageMetrics,\n filterIdeCompletionMetrics,\n filterIdeEditorMetrics,\n filterNewMetricsV2,\n} from '../utils/metricHelpers';\nimport { TaskOptions } from './TaskManagement';\n\nexport async function discoverEnterpriseTeamMetrics({\n api,\n logger,\n db,\n config,\n}: TaskOptions): Promise<void> {\n if (!config.getOptionalString('copilot.enterprise')) {\n logger.info(\n '[discoverEnterpriseTeamMetrics] Skipping: Enterprise configuration not found.',\n );\n return;\n }\n\n const type: MetricsType = 'enterprise';\n\n try {\n const teams = await api.fetchEnterpriseTeams();\n logger.info(\n `[discoverEnterpriseTeamMetrics] Fetched ${teams.length} teams`,\n );\n\n for (const team of teams) {\n try {\n logger.info(\n `[discoverEnterpriseTeamMetrics] Fetching metrics for team: ${team.slug}`,\n );\n\n const copilotMetrics = await api.fetchEnterpriseTeamCopilotMetrics(\n team.slug,\n );\n\n const lastDay = await db.getMostRecentDayFromMetricsV2(type, team.slug);\n logger.info(\n `[discoverEnterpriseTeamMetrics] Found last day: ${lastDay}`,\n );\n\n const newMetrics: CopilotMetrics[] = filterNewMetricsV2(\n copilotMetrics,\n lastDay,\n );\n logger.info(\n `[discoverEnterpriseTeamMetrics] Found ${newMetrics.length} new metrics to insert for team: ${team.slug}`,\n );\n\n if (newMetrics.length > 0) {\n const coPilotMetrics = filterBaseMetrics(newMetrics, type, team.slug);\n const ideCompletionsToInsert = filterIdeCompletionMetrics(\n newMetrics,\n type,\n team.slug,\n );\n const ideCompletionsLanguagesToInsert =\n filterIdeCompletionLanguageMetrics(newMetrics, type, team.slug);\n const ideCompletionsEditorsToInsert =\n filterIdeCompletionEditorMetrics(newMetrics, type, team.slug);\n const ideCompletionsEditorModelsToInsert =\n filterIdeCompletionEditorModelMetrics(newMetrics, type, team.slug);\n const ideCompletionsEditorModelLanguagesToInsert =\n filterIdeCompletionEditorModelLanguageMetrics(\n newMetrics,\n type,\n team.slug,\n );\n const ideChats = filterIdeChatMetrics(newMetrics, type, team.slug);\n const ideChatEditors = filterIdeEditorMetrics(\n newMetrics,\n type,\n team.slug,\n );\n const ideChatEditorModels = filterIdeChatEditorModelMetrics(\n newMetrics,\n type,\n team.slug,\n );\n\n await batchInsertInChunks(coPilotMetrics, 30, async chunk => {\n await db.batchInsertMetrics(chunk);\n });\n\n await batchInsertInChunks(ideCompletionsToInsert, 30, async chunk => {\n await db.batchInsertIdeCompletions(chunk);\n });\n\n await batchInsertInChunks(\n ideCompletionsLanguagesToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsLanguages(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorsToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditors(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorModelsToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditorModels(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorModelLanguagesToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditorModelLanguages(chunk);\n },\n );\n\n await batchInsertInChunks(ideChats, 30, async chunk => {\n await db.batchInsertIdeChats(chunk);\n });\n\n await batchInsertInChunks(ideChatEditors, 30, async chunk => {\n await db.batchInsertIdeChatEditors(chunk);\n });\n\n await batchInsertInChunks(ideChatEditorModels, 30, async chunk => {\n await db.batchInsertIdeChatEditorModels(chunk);\n });\n\n // Fetch seat analysis\n const seats = await api.fetchEnterpriseSeats();\n const seatsToInsert = convertToTeamSeatAnalysis(\n seats,\n type,\n team.slug,\n );\n await db.insertSeatAnalysys(seatsToInsert);\n\n logger.info(\n `[discoverEnterpriseTeamMetrics] Inserted new metrics into the database for team: ${team.slug}`,\n );\n } else {\n logger.info(\n `[discoverEnterpriseTeamMetrics] No new metrics found to insert for team: ${team.slug}`,\n );\n }\n } catch (error) {\n logger.error(\n `[discoverEnterpriseTeamMetrics] Error processing metrics for team ${team.slug}: ${error}`,\n );\n }\n }\n } catch (error) {\n logger.error(\n `[discoverEnterpriseTeamMetrics] Error fetching teams: ${error}`,\n );\n throw error;\n }\n}\n"],"names":["filterNewMetricsV2","filterBaseMetrics","filterIdeCompletionMetrics","filterIdeCompletionLanguageMetrics","filterIdeCompletionEditorMetrics","filterIdeCompletionEditorModelMetrics","filterIdeCompletionEditorModelLanguageMetrics","filterIdeChatMetrics","filterIdeEditorMetrics","filterIdeChatEditorModelMetrics","batchInsertInChunks","convertToTeamSeatAnalysis"],"mappings":"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"}
@@ -92,6 +92,9 @@ async function discoverOrganizationMetrics({
92
92
  await batchInsert.batchInsertInChunks(ideChatEditorModels, 30, async (chunk) => {
93
93
  await db.batchInsertIdeChatEditorModels(chunk);
94
94
  });
95
+ const seats = await api.fetchOrganizationSeats();
96
+ const seatsToInsert = metricHelpers.convertToSeatAnalysis(seats, type);
97
+ await db.insertSeatAnalysys(seatsToInsert);
95
98
  logger.info(
96
99
  "[discoverOrganizationMetrics] Inserted new metrics into the database"
97
100
  );
@@ -1 +1 @@
1
- {"version":3,"file":"OrganizationTask.cjs.js","sources":["../../src/task/OrganizationTask.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n MetricsType,\n CopilotMetrics,\n} from '@backstage-community/plugin-copilot-common';\nimport { batchInsertInChunks } from '../utils/batchInsert';\nimport {\n filterBaseMetrics,\n filterNewMetricsV2,\n filterIdeCompletionMetrics,\n filterIdeCompletionLanguageMetrics,\n filterIdeCompletionEditorMetrics,\n filterIdeCompletionEditorModelMetrics,\n filterIdeCompletionEditorModelLanguageMetrics,\n filterIdeChatMetrics,\n filterIdeEditorMetrics,\n filterIdeChatEditorModelMetrics,\n} from '../utils/metricHelpers';\nimport { TaskOptions } from './TaskManagement';\n\nexport async function discoverOrganizationMetrics({\n api,\n logger,\n db,\n config,\n}: TaskOptions): Promise<void> {\n if (!config.getOptionalString('copilot.organization')) {\n logger.info(\n '[discoverOrganizationMetrics] Skipping: Organization configuration not found.',\n );\n return;\n }\n\n const type: MetricsType = 'organization';\n\n try {\n const copilotMetrics = await api.fetchOrganizationCopilotMetrics();\n logger.info(\n `[discoverOrganizationMetrics] Fetched ${copilotMetrics.length} metrics`,\n );\n\n const lastDay = await db.getMostRecentDayFromMetricsV2(type);\n logger.info(`[discoverOrganizationMetrics] Found last day: ${lastDay}`);\n\n const newMetrics: CopilotMetrics[] = filterNewMetricsV2(\n copilotMetrics,\n lastDay,\n );\n logger.info(\n `[discoverOrganizationMetrics] Found ${newMetrics.length} new metrics to insert`,\n );\n\n if (newMetrics.length > 0) {\n const coPilotMetrics = filterBaseMetrics(newMetrics, type);\n const ideCompletionsToInsert = filterIdeCompletionMetrics(\n newMetrics,\n type,\n );\n const ideCompletionsLanguagesToInsert =\n filterIdeCompletionLanguageMetrics(newMetrics, type);\n const ideCompletionsEditorsToInsert = filterIdeCompletionEditorMetrics(\n newMetrics,\n type,\n );\n const ideCompletionsEditorModelsToInsert =\n filterIdeCompletionEditorModelMetrics(newMetrics, type);\n const ideCompletionsEditorModelLanguagesToInsert =\n filterIdeCompletionEditorModelLanguageMetrics(newMetrics, type);\n const ideChats = filterIdeChatMetrics(newMetrics, type);\n const ideChatEditors = filterIdeEditorMetrics(newMetrics, type);\n const ideChatEditorModels = filterIdeChatEditorModelMetrics(\n newMetrics,\n type,\n );\n\n await batchInsertInChunks(coPilotMetrics, 30, async chunk => {\n await db.batchInsertMetrics(chunk);\n });\n\n await batchInsertInChunks(ideCompletionsToInsert, 30, async chunk => {\n await db.batchInsertIdeCompletions(chunk);\n });\n\n await batchInsertInChunks(\n ideCompletionsLanguagesToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsLanguages(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorsToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditors(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorModelsToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditorModels(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorModelLanguagesToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditorModelLanguages(chunk);\n },\n );\n\n await batchInsertInChunks(ideChats, 30, async chunk => {\n await db.batchInsertIdeChats(chunk);\n });\n\n await batchInsertInChunks(ideChatEditors, 30, async chunk => {\n await db.batchInsertIdeChatEditors(chunk);\n });\n\n await batchInsertInChunks(ideChatEditorModels, 30, async chunk => {\n await db.batchInsertIdeChatEditorModels(chunk);\n });\n\n logger.info(\n '[discoverOrganizationMetrics] Inserted new metrics into the database',\n );\n } else {\n logger.info(\n '[discoverOrganizationMetrics] No new metrics found to insert',\n );\n }\n } catch (error) {\n logger.error(\n `[discoverOrganizationMetrics] An error occurred while processing Github Copilot metrics: ${error}`,\n );\n throw error;\n }\n}\n"],"names":["filterNewMetricsV2","filterBaseMetrics","filterIdeCompletionMetrics","filterIdeCompletionLanguageMetrics","filterIdeCompletionEditorMetrics","filterIdeCompletionEditorModelMetrics","filterIdeCompletionEditorModelLanguageMetrics","filterIdeChatMetrics","filterIdeEditorMetrics","filterIdeChatEditorModelMetrics","batchInsertInChunks"],"mappings":"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"}
1
+ {"version":3,"file":"OrganizationTask.cjs.js","sources":["../../src/task/OrganizationTask.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n MetricsType,\n CopilotMetrics,\n} from '@backstage-community/plugin-copilot-common';\nimport { batchInsertInChunks } from '../utils/batchInsert';\nimport {\n filterBaseMetrics,\n filterNewMetricsV2,\n filterIdeCompletionMetrics,\n filterIdeCompletionLanguageMetrics,\n filterIdeCompletionEditorMetrics,\n filterIdeCompletionEditorModelMetrics,\n filterIdeCompletionEditorModelLanguageMetrics,\n filterIdeChatMetrics,\n filterIdeEditorMetrics,\n filterIdeChatEditorModelMetrics,\n convertToSeatAnalysis,\n} from '../utils/metricHelpers';\nimport { TaskOptions } from './TaskManagement';\n\nexport async function discoverOrganizationMetrics({\n api,\n logger,\n db,\n config,\n}: TaskOptions): Promise<void> {\n if (!config.getOptionalString('copilot.organization')) {\n logger.info(\n '[discoverOrganizationMetrics] Skipping: Organization configuration not found.',\n );\n return;\n }\n\n const type: MetricsType = 'organization';\n\n try {\n const copilotMetrics = await api.fetchOrganizationCopilotMetrics();\n logger.info(\n `[discoverOrganizationMetrics] Fetched ${copilotMetrics.length} metrics`,\n );\n\n const lastDay = await db.getMostRecentDayFromMetricsV2(type);\n logger.info(`[discoverOrganizationMetrics] Found last day: ${lastDay}`);\n\n const newMetrics: CopilotMetrics[] = filterNewMetricsV2(\n copilotMetrics,\n lastDay,\n );\n logger.info(\n `[discoverOrganizationMetrics] Found ${newMetrics.length} new metrics to insert`,\n );\n\n if (newMetrics.length > 0) {\n const coPilotMetrics = filterBaseMetrics(newMetrics, type);\n const ideCompletionsToInsert = filterIdeCompletionMetrics(\n newMetrics,\n type,\n );\n const ideCompletionsLanguagesToInsert =\n filterIdeCompletionLanguageMetrics(newMetrics, type);\n const ideCompletionsEditorsToInsert = filterIdeCompletionEditorMetrics(\n newMetrics,\n type,\n );\n const ideCompletionsEditorModelsToInsert =\n filterIdeCompletionEditorModelMetrics(newMetrics, type);\n const ideCompletionsEditorModelLanguagesToInsert =\n filterIdeCompletionEditorModelLanguageMetrics(newMetrics, type);\n const ideChats = filterIdeChatMetrics(newMetrics, type);\n const ideChatEditors = filterIdeEditorMetrics(newMetrics, type);\n const ideChatEditorModels = filterIdeChatEditorModelMetrics(\n newMetrics,\n type,\n );\n\n await batchInsertInChunks(coPilotMetrics, 30, async chunk => {\n await db.batchInsertMetrics(chunk);\n });\n\n await batchInsertInChunks(ideCompletionsToInsert, 30, async chunk => {\n await db.batchInsertIdeCompletions(chunk);\n });\n\n await batchInsertInChunks(\n ideCompletionsLanguagesToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsLanguages(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorsToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditors(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorModelsToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditorModels(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorModelLanguagesToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditorModelLanguages(chunk);\n },\n );\n\n await batchInsertInChunks(ideChats, 30, async chunk => {\n await db.batchInsertIdeChats(chunk);\n });\n\n await batchInsertInChunks(ideChatEditors, 30, async chunk => {\n await db.batchInsertIdeChatEditors(chunk);\n });\n\n await batchInsertInChunks(ideChatEditorModels, 30, async chunk => {\n await db.batchInsertIdeChatEditorModels(chunk);\n });\n\n const seats = await api.fetchOrganizationSeats();\n const seatsToInsert = convertToSeatAnalysis(seats, type);\n await db.insertSeatAnalysys(seatsToInsert);\n\n logger.info(\n '[discoverOrganizationMetrics] Inserted new metrics into the database',\n );\n } else {\n logger.info(\n '[discoverOrganizationMetrics] No new metrics found to insert',\n );\n }\n } catch (error) {\n logger.error(\n `[discoverOrganizationMetrics] An error occurred while processing Github Copilot metrics: ${error}`,\n );\n throw error;\n }\n}\n"],"names":["filterNewMetricsV2","filterBaseMetrics","filterIdeCompletionMetrics","filterIdeCompletionLanguageMetrics","filterIdeCompletionEditorMetrics","filterIdeCompletionEditorModelMetrics","filterIdeCompletionEditorModelLanguageMetrics","filterIdeChatMetrics","filterIdeEditorMetrics","filterIdeChatEditorModelMetrics","batchInsertInChunks","convertToSeatAnalysis"],"mappings":"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"}
@@ -109,6 +109,13 @@ async function discoverOrganizationTeamMetrics({
109
109
  await batchInsert.batchInsertInChunks(ideChatEditorModels, 30, async (chunk) => {
110
110
  await db.batchInsertIdeChatEditorModels(chunk);
111
111
  });
112
+ const seats = await api.fetchOrganizationSeats();
113
+ const seatsToInsert = metricHelpers.convertToTeamSeatAnalysis(
114
+ seats,
115
+ type,
116
+ team.slug
117
+ );
118
+ await db.insertSeatAnalysys(seatsToInsert);
112
119
  logger.info(
113
120
  `[discoverOrganizationTeamMetrics] Inserted new metrics into the database for team: ${team.slug}`
114
121
  );
@@ -1 +1 @@
1
- {"version":3,"file":"OrganizationTeamTask.cjs.js","sources":["../../src/task/OrganizationTeamTask.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n CopilotMetrics,\n MetricsType,\n} from '@backstage-community/plugin-copilot-common';\nimport { batchInsertInChunks } from '../utils/batchInsert';\nimport {\n filterBaseMetrics,\n filterIdeChatEditorModelMetrics,\n filterIdeChatMetrics,\n filterIdeCompletionEditorMetrics,\n filterIdeCompletionEditorModelLanguageMetrics,\n filterIdeCompletionEditorModelMetrics,\n filterIdeCompletionLanguageMetrics,\n filterIdeCompletionMetrics,\n filterIdeEditorMetrics,\n filterNewMetricsV2,\n} from '../utils/metricHelpers';\nimport { TaskOptions } from './TaskManagement';\n\nexport async function discoverOrganizationTeamMetrics({\n api,\n logger,\n db,\n config,\n}: TaskOptions): Promise<void> {\n if (!config.getOptionalString('copilot.organization')) {\n logger.info(\n '[discoverOrganizationTeamMetrics] Skipping: Organization configuration not found.',\n );\n return;\n }\n\n const type: MetricsType = 'organization';\n\n try {\n const teams = await api.fetchOrganizationTeams();\n logger.info(\n `[discoverOrganizationTeamMetrics] Fetched ${teams.length} teams`,\n );\n\n for (const team of teams) {\n try {\n logger.info(\n `[discoverOrganizationTeamMetrics] Fetching metrics for team: ${team.slug}`,\n );\n\n const copilotMetrics = await api.fetchOrganizationTeamCopilotMetrics(\n team.slug,\n );\n\n const lastDay = await db.getMostRecentDayFromMetricsV2(type, team.slug);\n logger.info(\n `[discoverOrganizationTeamMetrics] Found last day: ${lastDay}`,\n );\n\n const newMetrics: CopilotMetrics[] = filterNewMetricsV2(\n copilotMetrics,\n lastDay,\n );\n logger.info(\n `[discoverOrganizationTeamMetrics] Found ${newMetrics.length} new metrics to insert for team: ${team.slug}`,\n );\n\n if (newMetrics.length > 0) {\n const coPilotMetrics = filterBaseMetrics(newMetrics, type, team.slug);\n const ideCompletionsToInsert = filterIdeCompletionMetrics(\n newMetrics,\n type,\n team.slug,\n );\n const ideCompletionsLanguagesToInsert =\n filterIdeCompletionLanguageMetrics(newMetrics, type, team.slug);\n const ideCompletionsEditorsToInsert =\n filterIdeCompletionEditorMetrics(newMetrics, type, team.slug);\n const ideCompletionsEditorModelsToInsert =\n filterIdeCompletionEditorModelMetrics(newMetrics, type, team.slug);\n const ideCompletionsEditorModelLanguagesToInsert =\n filterIdeCompletionEditorModelLanguageMetrics(\n newMetrics,\n type,\n team.slug,\n );\n const ideChats = filterIdeChatMetrics(newMetrics, type, team.slug);\n const ideChatEditors = filterIdeEditorMetrics(\n newMetrics,\n type,\n team.slug,\n );\n const ideChatEditorModels = filterIdeChatEditorModelMetrics(\n newMetrics,\n type,\n team.slug,\n );\n\n await batchInsertInChunks(coPilotMetrics, 30, async chunk => {\n await db.batchInsertMetrics(chunk);\n });\n\n await batchInsertInChunks(ideCompletionsToInsert, 30, async chunk => {\n await db.batchInsertIdeCompletions(chunk);\n });\n\n await batchInsertInChunks(\n ideCompletionsLanguagesToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsLanguages(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorsToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditors(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorModelsToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditorModels(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorModelLanguagesToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditorModelLanguages(chunk);\n },\n );\n\n await batchInsertInChunks(ideChats, 30, async chunk => {\n await db.batchInsertIdeChats(chunk);\n });\n\n await batchInsertInChunks(ideChatEditors, 30, async chunk => {\n await db.batchInsertIdeChatEditors(chunk);\n });\n\n await batchInsertInChunks(ideChatEditorModels, 30, async chunk => {\n await db.batchInsertIdeChatEditorModels(chunk);\n });\n\n logger.info(\n `[discoverOrganizationTeamMetrics] Inserted new metrics into the database for team: ${team.slug}`,\n );\n } else {\n logger.info(\n `[discoverOrganizationTeamMetrics] No new metrics found to insert for team: ${team.slug}`,\n );\n }\n } catch (error) {\n logger.error(\n `[discoverOrganizationTeamMetrics] Error processing metrics for team ${team.slug}: ${error}`,\n );\n }\n }\n } catch (error) {\n logger.error(\n `[discoverOrganizationTeamMetrics] Error fetching teams: ${error}`,\n );\n throw error;\n }\n}\n"],"names":["filterNewMetricsV2","filterBaseMetrics","filterIdeCompletionMetrics","filterIdeCompletionLanguageMetrics","filterIdeCompletionEditorMetrics","filterIdeCompletionEditorModelMetrics","filterIdeCompletionEditorModelLanguageMetrics","filterIdeChatMetrics","filterIdeEditorMetrics","filterIdeChatEditorModelMetrics","batchInsertInChunks"],"mappings":"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"}
1
+ {"version":3,"file":"OrganizationTeamTask.cjs.js","sources":["../../src/task/OrganizationTeamTask.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n CopilotMetrics,\n MetricsType,\n} from '@backstage-community/plugin-copilot-common';\nimport { batchInsertInChunks } from '../utils/batchInsert';\nimport {\n convertToTeamSeatAnalysis,\n filterBaseMetrics,\n filterIdeChatEditorModelMetrics,\n filterIdeChatMetrics,\n filterIdeCompletionEditorMetrics,\n filterIdeCompletionEditorModelLanguageMetrics,\n filterIdeCompletionEditorModelMetrics,\n filterIdeCompletionLanguageMetrics,\n filterIdeCompletionMetrics,\n filterIdeEditorMetrics,\n filterNewMetricsV2,\n} from '../utils/metricHelpers';\nimport { TaskOptions } from './TaskManagement';\n\nexport async function discoverOrganizationTeamMetrics({\n api,\n logger,\n db,\n config,\n}: TaskOptions): Promise<void> {\n if (!config.getOptionalString('copilot.organization')) {\n logger.info(\n '[discoverOrganizationTeamMetrics] Skipping: Organization configuration not found.',\n );\n return;\n }\n\n const type: MetricsType = 'organization';\n\n try {\n const teams = await api.fetchOrganizationTeams();\n logger.info(\n `[discoverOrganizationTeamMetrics] Fetched ${teams.length} teams`,\n );\n\n for (const team of teams) {\n try {\n logger.info(\n `[discoverOrganizationTeamMetrics] Fetching metrics for team: ${team.slug}`,\n );\n\n const copilotMetrics = await api.fetchOrganizationTeamCopilotMetrics(\n team.slug,\n );\n\n const lastDay = await db.getMostRecentDayFromMetricsV2(type, team.slug);\n logger.info(\n `[discoverOrganizationTeamMetrics] Found last day: ${lastDay}`,\n );\n\n const newMetrics: CopilotMetrics[] = filterNewMetricsV2(\n copilotMetrics,\n lastDay,\n );\n logger.info(\n `[discoverOrganizationTeamMetrics] Found ${newMetrics.length} new metrics to insert for team: ${team.slug}`,\n );\n\n if (newMetrics.length > 0) {\n const coPilotMetrics = filterBaseMetrics(newMetrics, type, team.slug);\n const ideCompletionsToInsert = filterIdeCompletionMetrics(\n newMetrics,\n type,\n team.slug,\n );\n const ideCompletionsLanguagesToInsert =\n filterIdeCompletionLanguageMetrics(newMetrics, type, team.slug);\n const ideCompletionsEditorsToInsert =\n filterIdeCompletionEditorMetrics(newMetrics, type, team.slug);\n const ideCompletionsEditorModelsToInsert =\n filterIdeCompletionEditorModelMetrics(newMetrics, type, team.slug);\n const ideCompletionsEditorModelLanguagesToInsert =\n filterIdeCompletionEditorModelLanguageMetrics(\n newMetrics,\n type,\n team.slug,\n );\n const ideChats = filterIdeChatMetrics(newMetrics, type, team.slug);\n const ideChatEditors = filterIdeEditorMetrics(\n newMetrics,\n type,\n team.slug,\n );\n const ideChatEditorModels = filterIdeChatEditorModelMetrics(\n newMetrics,\n type,\n team.slug,\n );\n\n await batchInsertInChunks(coPilotMetrics, 30, async chunk => {\n await db.batchInsertMetrics(chunk);\n });\n\n await batchInsertInChunks(ideCompletionsToInsert, 30, async chunk => {\n await db.batchInsertIdeCompletions(chunk);\n });\n\n await batchInsertInChunks(\n ideCompletionsLanguagesToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsLanguages(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorsToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditors(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorModelsToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditorModels(chunk);\n },\n );\n\n await batchInsertInChunks(\n ideCompletionsEditorModelLanguagesToInsert,\n 30,\n async chunk => {\n await db.batchInsertIdeCompletionsEditorModelLanguages(chunk);\n },\n );\n\n await batchInsertInChunks(ideChats, 30, async chunk => {\n await db.batchInsertIdeChats(chunk);\n });\n\n await batchInsertInChunks(ideChatEditors, 30, async chunk => {\n await db.batchInsertIdeChatEditors(chunk);\n });\n\n await batchInsertInChunks(ideChatEditorModels, 30, async chunk => {\n await db.batchInsertIdeChatEditorModels(chunk);\n });\n\n // Fetch seat analysis\n const seats = await api.fetchOrganizationSeats();\n const seatsToInsert = convertToTeamSeatAnalysis(\n seats,\n type,\n team.slug,\n );\n await db.insertSeatAnalysys(seatsToInsert);\n\n logger.info(\n `[discoverOrganizationTeamMetrics] Inserted new metrics into the database for team: ${team.slug}`,\n );\n } else {\n logger.info(\n `[discoverOrganizationTeamMetrics] No new metrics found to insert for team: ${team.slug}`,\n );\n }\n } catch (error) {\n logger.error(\n `[discoverOrganizationTeamMetrics] Error processing metrics for team ${team.slug}: ${error}`,\n );\n }\n }\n } catch (error) {\n logger.error(\n `[discoverOrganizationTeamMetrics] Error fetching teams: ${error}`,\n );\n throw error;\n }\n}\n"],"names":["filterNewMetricsV2","filterBaseMetrics","filterIdeCompletionMetrics","filterIdeCompletionLanguageMetrics","filterIdeCompletionEditorMetrics","filterIdeCompletionEditorModelMetrics","filterIdeCompletionEditorModelLanguageMetrics","filterIdeChatMetrics","filterIdeEditorMetrics","filterIdeChatEditorModelMetrics","batchInsertInChunks","convertToTeamSeatAnalysis"],"mappings":"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"}
@@ -121,7 +121,51 @@ function filterIdeChatEditorModelMetrics(metrics, type, team) {
121
121
  ) || []
122
122
  ).filter((model) => model.total_engaged_users > 0);
123
123
  }
124
+ function convertToSeatAnalysis(metrics, type, team) {
125
+ const totalSeats = metrics.total_seats;
126
+ const today = luxon.DateTime.now().startOf("day");
127
+ const seatsNeverUsed = metrics.seats.filter(
128
+ (seat) => !seat.last_activity_at
129
+ ).length;
130
+ const seatsInactive7Days = metrics.seats.filter((seat) => {
131
+ if (!seat.last_activity_at) return false;
132
+ const lastActivityDate = luxon.DateTime.fromISO(seat.last_activity_at);
133
+ return today.diff(lastActivityDate, "days").days >= 7;
134
+ }).length;
135
+ const seatsInactive14Days = metrics.seats.filter((seat) => {
136
+ if (!seat.last_activity_at) return false;
137
+ const lastActivityDate = luxon.DateTime.fromISO(seat.last_activity_at);
138
+ return today.diff(lastActivityDate, "days").days >= 14;
139
+ }).length;
140
+ const seatsInactive28Days = metrics.seats.filter((seat) => {
141
+ if (!seat.last_activity_at) return false;
142
+ const lastActivityDate = luxon.DateTime.fromISO(seat.last_activity_at);
143
+ return today.diff(lastActivityDate, "days").days >= 28;
144
+ }).length;
145
+ return {
146
+ day: today.toISODate(),
147
+ type,
148
+ team_name: team ?? "",
149
+ total_seats: totalSeats,
150
+ seats_never_used: seatsNeverUsed,
151
+ seats_inactive_7_days: seatsInactive7Days,
152
+ seats_inactive_14_days: seatsInactive14Days,
153
+ seats_inactive_28_days: seatsInactive28Days
154
+ };
155
+ }
156
+ function convertToTeamSeatAnalysis(metrics, type, team) {
157
+ const teamSeatMetrics = metrics.seats.filter(
158
+ (seat) => seat.assigning_team?.slug === team
159
+ );
160
+ const teamMetrics = {
161
+ total_seats: teamSeatMetrics.length,
162
+ seats: teamSeatMetrics
163
+ };
164
+ return convertToSeatAnalysis(teamMetrics, type, team);
165
+ }
124
166
 
167
+ exports.convertToSeatAnalysis = convertToSeatAnalysis;
168
+ exports.convertToTeamSeatAnalysis = convertToTeamSeatAnalysis;
125
169
  exports.filterBaseMetrics = filterBaseMetrics;
126
170
  exports.filterIdeChatEditorModelMetrics = filterIdeChatEditorModelMetrics;
127
171
  exports.filterIdeChatMetrics = filterIdeChatMetrics;
@@ -1 +1 @@
1
- {"version":3,"file":"metricHelpers.cjs.js","sources":["../../src/utils/metricHelpers.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { DateTime } from 'luxon';\nimport {\n CopilotIdeChatsDb,\n CopilotIdeChatsEditorModelDb,\n CopilotIdeChatsEditorsDb,\n CopilotIdeCodeCompletionsDb,\n CopilotIdeCodeCompletionsEditorModelLanguagesDb,\n CopilotIdeCodeCompletionsEditorModelsDb,\n CopilotIdeCodeCompletionsEditorsDb,\n CopilotIdeCodeCompletionsLanguageDb,\n CopilotMetricsDb,\n MetricDbRow,\n} from '../db/DatabaseHandler';\nimport {\n Metric,\n MetricsType,\n CopilotMetrics,\n} from '@backstage-community/plugin-copilot-common';\n\nexport function filterNewMetricsV2(\n metrics: CopilotMetrics[],\n lastDay?: string,\n): CopilotMetrics[] {\n return metrics\n .sort(\n (a, b) =>\n DateTime.fromISO(a.date).toMillis() -\n DateTime.fromISO(b.date).toMillis(),\n )\n .filter(metric => {\n const metricDate = DateTime.fromISO(metric.date);\n\n const lastDayDate = lastDay\n ? DateTime.fromJSDate(new Date(lastDay))\n : null;\n\n return !lastDay || (lastDayDate?.isValid && metricDate > lastDayDate);\n });\n}\n\nexport function filterBaseMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotMetricsDb[] {\n return metrics\n .map(metric => ({\n day: metric.date,\n type: type,\n team_name: team,\n total_engaged_users: metric.total_engaged_users,\n total_active_users: metric.total_active_users,\n }))\n .filter(metric => metric.total_engaged_users > 0);\n}\n\nexport function filterIdeCompletionMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeCodeCompletionsDb[] {\n return metrics\n .map(metric => ({\n day: metric.date,\n type: type,\n team_name: team,\n total_engaged_users:\n metric.copilot_ide_code_completions.total_engaged_users,\n }))\n .filter(completion => completion.total_engaged_users > 0);\n}\n\nexport function filterIdeCompletionLanguageMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeCodeCompletionsLanguageDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_code_completions.languages?.map(language => ({\n day: metric.date,\n type: type,\n team_name: team,\n language: language.name,\n total_engaged_users: language.total_engaged_users,\n })) || [],\n )\n .filter(language => language.total_engaged_users > 0);\n}\nexport function filterIdeCompletionEditorMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeCodeCompletionsEditorsDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_code_completions.editors?.map(editor => ({\n day: metric.date,\n type: type,\n team_name: team,\n editor: editor.name,\n total_engaged_users: editor.total_engaged_users,\n })) || [],\n )\n .filter(editor => editor.total_engaged_users > 0);\n}\n\nexport function filterIdeCompletionEditorModelMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeCodeCompletionsEditorModelsDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_code_completions.editors?.flatMap(editor =>\n editor.models.map(model => ({\n day: metric.date,\n type: type,\n team_name: team,\n editor: editor.name,\n model: model.name,\n total_engaged_users: model.total_engaged_users,\n })),\n ) || [],\n )\n .filter(model => model.total_engaged_users > 0);\n}\nexport function filterIdeCompletionEditorModelLanguageMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeCodeCompletionsEditorModelLanguagesDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_code_completions.editors?.flatMap(editor =>\n editor.models.flatMap(model =>\n model.languages.map(language => ({\n day: metric.date,\n type: type,\n team_name: team,\n editor: editor.name,\n model: model.name,\n language: language.name,\n total_engaged_users: language.total_engaged_users,\n total_code_acceptances: language.total_code_acceptances,\n total_code_suggestions: language.total_code_suggestions,\n total_code_lines_accepted: language.total_code_lines_accepted,\n total_code_lines_suggested: language.total_code_lines_suggested,\n })),\n ),\n ) || [],\n )\n .filter(language => language.total_engaged_users > 0);\n}\n\nexport function filterIdeChatMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeChatsDb[] {\n return metrics\n .map((metric: CopilotMetrics) => ({\n day: metric.date,\n type: type,\n team_name: team,\n total_engaged_users: metric.copilot_ide_chat.total_engaged_users,\n }))\n .filter(chat => chat.total_engaged_users > 0);\n}\n\nexport function filterIdeEditorMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeChatsEditorsDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_chat.editors?.map(editor => ({\n day: metric.date,\n type: type,\n team_name: team,\n editor: editor.name,\n total_engaged_users: editor.total_engaged_users,\n })) || [],\n )\n .filter(editor => editor.total_engaged_users > 0);\n}\n\nexport function filterIdeChatEditorModelMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeChatsEditorModelDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_chat.editors?.flatMap(editor =>\n editor.models.map(model => ({\n day: metric.date,\n type: type,\n team_name: team,\n editor: editor.name,\n model: model.name,\n total_engaged_users: model.total_engaged_users,\n total_chat_copy_events: model.total_chat_copy_events,\n total_chats: model.total_chats,\n total_chat_insertion_events: model.total_chat_insertion_events,\n })),\n ) || [],\n )\n .filter(model => model.total_engaged_users > 0);\n}\n\nexport function prepareMetricsForInsert(\n metrics: Metric[],\n type: MetricsType,\n team_name?: string,\n): MetricDbRow[] {\n return metrics.map(({ breakdown, ...rest }) => ({\n ...rest,\n type,\n team_name,\n breakdown: JSON.stringify(breakdown),\n })) as MetricDbRow[];\n}\n"],"names":["DateTime"],"mappings":"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"}
1
+ {"version":3,"file":"metricHelpers.cjs.js","sources":["../../src/utils/metricHelpers.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { DateTime } from 'luxon';\nimport {\n CopilotIdeChatsDb,\n CopilotIdeChatsEditorModelDb,\n CopilotIdeChatsEditorsDb,\n CopilotIdeCodeCompletionsDb,\n CopilotIdeCodeCompletionsEditorModelLanguagesDb,\n CopilotIdeCodeCompletionsEditorModelsDb,\n CopilotIdeCodeCompletionsEditorsDb,\n CopilotIdeCodeCompletionsLanguageDb,\n CopilotMetricsDb,\n MetricDbRow,\n} from '../db/DatabaseHandler';\nimport {\n Metric,\n MetricsType,\n CopilotMetrics,\n CopilotSeats,\n SeatAnalysis,\n} from '@backstage-community/plugin-copilot-common';\n\nexport function filterNewMetricsV2(\n metrics: CopilotMetrics[],\n lastDay?: string,\n): CopilotMetrics[] {\n return metrics\n .sort(\n (a, b) =>\n DateTime.fromISO(a.date).toMillis() -\n DateTime.fromISO(b.date).toMillis(),\n )\n .filter(metric => {\n const metricDate = DateTime.fromISO(metric.date);\n\n const lastDayDate = lastDay\n ? DateTime.fromJSDate(new Date(lastDay))\n : null;\n\n return !lastDay || (lastDayDate?.isValid && metricDate > lastDayDate);\n });\n}\n\nexport function filterBaseMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotMetricsDb[] {\n return metrics\n .map(metric => ({\n day: metric.date,\n type: type,\n team_name: team,\n total_engaged_users: metric.total_engaged_users,\n total_active_users: metric.total_active_users,\n }))\n .filter(metric => metric.total_engaged_users > 0);\n}\n\nexport function filterIdeCompletionMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeCodeCompletionsDb[] {\n return metrics\n .map(metric => ({\n day: metric.date,\n type: type,\n team_name: team,\n total_engaged_users:\n metric.copilot_ide_code_completions.total_engaged_users,\n }))\n .filter(completion => completion.total_engaged_users > 0);\n}\n\nexport function filterIdeCompletionLanguageMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeCodeCompletionsLanguageDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_code_completions.languages?.map(language => ({\n day: metric.date,\n type: type,\n team_name: team,\n language: language.name,\n total_engaged_users: language.total_engaged_users,\n })) || [],\n )\n .filter(language => language.total_engaged_users > 0);\n}\nexport function filterIdeCompletionEditorMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeCodeCompletionsEditorsDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_code_completions.editors?.map(editor => ({\n day: metric.date,\n type: type,\n team_name: team,\n editor: editor.name,\n total_engaged_users: editor.total_engaged_users,\n })) || [],\n )\n .filter(editor => editor.total_engaged_users > 0);\n}\n\nexport function filterIdeCompletionEditorModelMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeCodeCompletionsEditorModelsDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_code_completions.editors?.flatMap(editor =>\n editor.models.map(model => ({\n day: metric.date,\n type: type,\n team_name: team,\n editor: editor.name,\n model: model.name,\n total_engaged_users: model.total_engaged_users,\n })),\n ) || [],\n )\n .filter(model => model.total_engaged_users > 0);\n}\nexport function filterIdeCompletionEditorModelLanguageMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeCodeCompletionsEditorModelLanguagesDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_code_completions.editors?.flatMap(editor =>\n editor.models.flatMap(model =>\n model.languages.map(language => ({\n day: metric.date,\n type: type,\n team_name: team,\n editor: editor.name,\n model: model.name,\n language: language.name,\n total_engaged_users: language.total_engaged_users,\n total_code_acceptances: language.total_code_acceptances,\n total_code_suggestions: language.total_code_suggestions,\n total_code_lines_accepted: language.total_code_lines_accepted,\n total_code_lines_suggested: language.total_code_lines_suggested,\n })),\n ),\n ) || [],\n )\n .filter(language => language.total_engaged_users > 0);\n}\n\nexport function filterIdeChatMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeChatsDb[] {\n return metrics\n .map((metric: CopilotMetrics) => ({\n day: metric.date,\n type: type,\n team_name: team,\n total_engaged_users: metric.copilot_ide_chat.total_engaged_users,\n }))\n .filter(chat => chat.total_engaged_users > 0);\n}\n\nexport function filterIdeEditorMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeChatsEditorsDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_chat.editors?.map(editor => ({\n day: metric.date,\n type: type,\n team_name: team,\n editor: editor.name,\n total_engaged_users: editor.total_engaged_users,\n })) || [],\n )\n .filter(editor => editor.total_engaged_users > 0);\n}\n\nexport function filterIdeChatEditorModelMetrics(\n metrics: CopilotMetrics[],\n type: MetricsType,\n team?: string,\n): CopilotIdeChatsEditorModelDb[] {\n return metrics\n .flatMap(\n (metric: CopilotMetrics) =>\n metric.copilot_ide_chat.editors?.flatMap(editor =>\n editor.models.map(model => ({\n day: metric.date,\n type: type,\n team_name: team,\n editor: editor.name,\n model: model.name,\n total_engaged_users: model.total_engaged_users,\n total_chat_copy_events: model.total_chat_copy_events,\n total_chats: model.total_chats,\n total_chat_insertion_events: model.total_chat_insertion_events,\n })),\n ) || [],\n )\n .filter(model => model.total_engaged_users > 0);\n}\n\nexport function prepareMetricsForInsert(\n metrics: Metric[],\n type: MetricsType,\n team_name?: string,\n): MetricDbRow[] {\n return metrics.map(({ breakdown, ...rest }) => ({\n ...rest,\n type,\n team_name,\n breakdown: JSON.stringify(breakdown),\n })) as MetricDbRow[];\n}\n\nexport function convertToSeatAnalysis(\n metrics: CopilotSeats,\n type: MetricsType,\n team?: string,\n): SeatAnalysis {\n const totalSeats = metrics.total_seats;\n const today = DateTime.now().startOf('day');\n\n // Count seats with no activity\n const seatsNeverUsed = metrics.seats.filter(\n seat => !seat.last_activity_at,\n ).length;\n\n // Count seats with no activity in the last 7, 14, and 28 days\n const seatsInactive7Days = metrics.seats.filter(seat => {\n if (!seat.last_activity_at) return false;\n const lastActivityDate = DateTime.fromISO(seat.last_activity_at);\n return today.diff(lastActivityDate, 'days').days >= 7;\n }).length;\n\n const seatsInactive14Days = metrics.seats.filter(seat => {\n if (!seat.last_activity_at) return false;\n const lastActivityDate = DateTime.fromISO(seat.last_activity_at);\n return today.diff(lastActivityDate, 'days').days >= 14;\n }).length;\n\n const seatsInactive28Days = metrics.seats.filter(seat => {\n if (!seat.last_activity_at) return false;\n const lastActivityDate = DateTime.fromISO(seat.last_activity_at);\n return today.diff(lastActivityDate, 'days').days >= 28;\n }).length;\n\n return {\n day: today.toISODate(),\n type,\n team_name: team ?? '',\n total_seats: totalSeats,\n seats_never_used: seatsNeverUsed,\n seats_inactive_7_days: seatsInactive7Days,\n seats_inactive_14_days: seatsInactive14Days,\n seats_inactive_28_days: seatsInactive28Days,\n };\n}\n\nexport function convertToTeamSeatAnalysis(\n metrics: CopilotSeats,\n type: MetricsType,\n team: string,\n): SeatAnalysis {\n const teamSeatMetrics = metrics.seats.filter(\n seat => seat.assigning_team?.slug === team,\n );\n const teamMetrics = {\n total_seats: teamSeatMetrics.length,\n seats: teamSeatMetrics,\n };\n\n return convertToSeatAnalysis(teamMetrics, type, team);\n}\n"],"names":["DateTime"],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /*
2
+ * Copyright 2024 The Backstage Authors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /**
17
+ * @param {import('knex').Knex} knex
18
+ */
19
+ exports.up = async function up(knex) {
20
+ await knex.schema.createTable('seats', table => {
21
+ table.comment('Table for storing metrics for CoPilot seat metrics');
22
+ table.date('day').notNullable().comment('Date of the metrics data');
23
+ table
24
+ .string('type')
25
+ .notNullable()
26
+ .defaultTo('organization')
27
+ .comment('The type of metric');
28
+ table.string('team_name', 255).notNullable().comment('Name of the team');
29
+ table.integer('total_seats').notNullable().comment('Total seats');
30
+ table
31
+ .integer('seats_never_used')
32
+ .notNullable()
33
+ .comment('Total seats never used');
34
+ table
35
+ .integer('seats_inactive_7_days')
36
+ .notNullable()
37
+ .comment('Total seats inactive for 7 days');
38
+ table
39
+ .integer('seats_inactive_14_days')
40
+ .notNullable()
41
+ .comment('Total seats inactive for 14 days');
42
+ table
43
+ .integer('seats_inactive_28_days')
44
+ .notNullable()
45
+ .comment('Total seats inactive for 28 days');
46
+ table.unique(['day', 'type', 'team_name'], { indexName: 'uk_seat_day' });
47
+ });
48
+ };
49
+
50
+ /**
51
+ * @param {import('knex').Knex} knex
52
+ */
53
+ exports.down = async function down(knex) {
54
+ await knex.schema.dropTable('seats');
55
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage-community/plugin-copilot-backend",
3
- "version": "0.8.0",
3
+ "version": "0.9.1",
4
4
  "homepage": "https://backstage.io",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/index.cjs.js",
@@ -42,13 +42,14 @@
42
42
  "postpack": "backstage-cli package postpack"
43
43
  },
44
44
  "dependencies": {
45
- "@backstage-community/plugin-copilot-common": "^0.8.0",
46
- "@backstage/backend-app-api": "^1.2.1",
47
- "@backstage/backend-defaults": "^0.8.2",
48
- "@backstage/backend-plugin-api": "^1.2.1",
45
+ "@backstage-community/plugin-copilot-common": "^0.9.1",
46
+ "@backstage/backend-app-api": "^1.2.2",
47
+ "@backstage/backend-defaults": "^0.9.0",
48
+ "@backstage/backend-plugin-api": "^1.3.0",
49
49
  "@backstage/config": "^1.3.2",
50
50
  "@backstage/errors": "^1.2.7",
51
- "@backstage/integration": "^1.16.2",
51
+ "@backstage/integration": "^1.16.3",
52
+ "@octokit/rest": "20.1.0",
52
53
  "@types/express": "*",
53
54
  "express": "^4.17.1",
54
55
  "express-promise-router": "^4.1.0",
@@ -60,15 +61,22 @@
60
61
  "zod": "^3.23.8"
61
62
  },
62
63
  "devDependencies": {
63
- "@backstage/backend-test-utils": "^1.3.1",
64
- "@backstage/cli": "^0.31.0",
65
- "@backstage/plugin-auth-backend": "^0.24.4",
66
- "@backstage/plugin-auth-backend-module-guest-provider": "^0.2.6",
67
- "@backstage/test-utils": "^1.7.6",
64
+ "@backstage/backend-test-utils": "^1.4.0",
65
+ "@backstage/cli": "^0.32.0",
66
+ "@backstage/plugin-auth-backend": "^0.24.5",
67
+ "@backstage/plugin-auth-backend-module-guest-provider": "^0.2.7",
68
+ "@backstage/test-utils": "^1.7.7",
68
69
  "@types/node-fetch": "^2.6.11",
69
70
  "@types/supertest": "^2.0.8",
70
71
  "msw": "^1.0.0",
71
72
  "supertest": "^6.2.4"
72
73
  },
73
- "configSchema": "config.d.ts"
74
+ "configSchema": "config.d.ts",
75
+ "typesVersions": {
76
+ "*": {
77
+ "package.json": [
78
+ "package.json"
79
+ ]
80
+ }
81
+ }
74
82
  }