@backstage-community/plugin-copilot-backend 0.12.0 → 0.14.0

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,28 @@
1
1
  # @backstage-community/plugin-copilot-backend
2
2
 
3
+ ## 0.14.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 8c60a7f: Backstage version bump to v1.43.2
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [8c60a7f]
12
+ - @backstage-community/plugin-copilot-common@0.13.0
13
+
14
+ ## 0.13.0
15
+
16
+ ### Minor Changes
17
+
18
+ - 181ea72: Various performance improvements for larger organizations
19
+
20
+ - Only gather metrics from teams that have 5 or more members, teams
21
+ with less members will not have any metrics provided by the api as
22
+ documented here https://docs.github.com/en/rest/copilot/copilot-metrics?apiVersion=2022-11-28
23
+ - To improve performance and reduce the API calls made on large orgs,
24
+ we only need to retrieve the org and enterprise seats once per task.
25
+
3
26
  ## 0.12.0
4
27
 
5
28
  ### Minor Changes
@@ -64,12 +64,59 @@ class GithubClient {
64
64
  }
65
65
  async fetchEnterpriseTeams() {
66
66
  const octokit = await this.getEnterpriseOctokit();
67
- const path = `/enterprises/${this.copilotConfig.enterprise}/teams`;
68
67
  try {
69
- const teams = await octokit.paginate(`GET ${path}`, {
70
- per_page: 100
71
- // Maximum allowed per page
72
- });
68
+ const teams = [];
69
+ let cursor = null;
70
+ let hasNextPage = true;
71
+ while (hasNextPage) {
72
+ const query = `
73
+ query($enterprise: String!, $cursor: String) {
74
+ enterprise(slug: $enterprise) {
75
+ organizations(first: 100) {
76
+ nodes {
77
+ teams(first: 100, after: $cursor) {
78
+ pageInfo {
79
+ hasNextPage
80
+ endCursor
81
+ }
82
+ nodes {
83
+ id
84
+ databaseId
85
+ slug
86
+ name
87
+ members {
88
+ totalCount
89
+ }
90
+ }
91
+ }
92
+ }
93
+ }
94
+ }
95
+ }
96
+ `;
97
+ const variables = {
98
+ enterprise: this.copilotConfig.enterprise,
99
+ cursor
100
+ };
101
+ const response = await octokit.graphql(query, variables);
102
+ const allTeams = response.enterprise.organizations.nodes.flatMap(
103
+ (org) => org.teams.nodes
104
+ );
105
+ const filteredTeams = allTeams.filter((team) => team.members.totalCount >= 5).map((team) => ({
106
+ id: team.databaseId,
107
+ slug: team.slug,
108
+ name: team.name
109
+ }));
110
+ teams.push(...filteredTeams);
111
+ hasNextPage = response.enterprise.organizations.nodes.some(
112
+ (org) => org.teams.pageInfo.hasNextPage
113
+ );
114
+ if (hasNextPage) {
115
+ cursor = response.enterprise.organizations.nodes.find(
116
+ (org) => org.teams.pageInfo.hasNextPage
117
+ )?.teams.pageInfo.endCursor;
118
+ }
119
+ }
73
120
  return teams;
74
121
  } catch (error) {
75
122
  throw errors.ResponseError.fromResponse(error.response || error);
@@ -110,12 +157,47 @@ class GithubClient {
110
157
  }
111
158
  async fetchOrganizationTeams() {
112
159
  const octokit = await this.getOrganizationOctokit();
113
- const path = `/orgs/${this.copilotConfig.organization}/teams`;
114
160
  try {
115
- const teams = await octokit.paginate(`GET ${path}`, {
116
- per_page: 100
117
- // Maximum allowed per page
118
- });
161
+ const teams = [];
162
+ let cursor = null;
163
+ let hasNextPage = true;
164
+ while (hasNextPage) {
165
+ const query = `
166
+ query($org: String!, $cursor: String) {
167
+ organization(login: $org) {
168
+ teams(first: 100, after: $cursor) {
169
+ pageInfo {
170
+ hasNextPage
171
+ endCursor
172
+ }
173
+ nodes {
174
+ id
175
+ databaseId
176
+ slug
177
+ name
178
+ members {
179
+ totalCount
180
+ }
181
+ }
182
+ }
183
+ }
184
+ }
185
+ `;
186
+ const variables = {
187
+ org: this.copilotConfig.organization,
188
+ cursor
189
+ };
190
+ const response = await octokit.graphql(query, variables);
191
+ const teamsData = response.organization.teams;
192
+ const filteredTeams = teamsData.nodes.filter((team) => team.members.totalCount >= 5).map((team) => ({
193
+ id: team.databaseId,
194
+ slug: team.slug,
195
+ name: team.name
196
+ }));
197
+ teams.push(...filteredTeams);
198
+ hasNextPage = teamsData.pageInfo.hasNextPage;
199
+ cursor = teamsData.pageInfo.endCursor;
200
+ }
119
201
  return teams;
120
202
  } catch (error) {
121
203
  throw errors.ResponseError.fromResponse(error.response || error);
@@ -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 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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"}
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\n try {\n const teams: TeamInfo[] = [];\n let cursor: string | null = null;\n let hasNextPage = true;\n\n while (hasNextPage) {\n const query = `\n query($enterprise: String!, $cursor: String) {\n enterprise(slug: $enterprise) {\n organizations(first: 100) {\n nodes {\n teams(first: 100, after: $cursor) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n id\n databaseId\n slug\n name\n members {\n totalCount\n }\n }\n }\n }\n }\n }\n }\n `;\n\n const variables = {\n enterprise: this.copilotConfig.enterprise,\n cursor,\n };\n\n const response: any = await octokit.graphql(query, variables);\n\n // Flatten teams from all organizations in the enterprise\n const allTeams = response.enterprise.organizations.nodes.flatMap(\n (org: any) => org.teams.nodes,\n );\n\n // Filter teams with 5 or more members\n const filteredTeams = allTeams\n .filter((team: any) => team.members.totalCount >= 5)\n .map((team: any) => ({\n id: team.databaseId,\n slug: team.slug,\n name: team.name,\n }));\n\n teams.push(...filteredTeams);\n\n // Check if any organization has more teams to fetch\n hasNextPage = response.enterprise.organizations.nodes.some(\n (org: any) => org.teams.pageInfo.hasNextPage,\n );\n\n if (hasNextPage) {\n cursor = response.enterprise.organizations.nodes.find(\n (org: any) => org.teams.pageInfo.hasNextPage,\n )?.teams.pageInfo.endCursor;\n }\n }\n\n return teams;\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\n try {\n const teams: TeamInfo[] = [];\n let cursor: string | null = null;\n let hasNextPage = true;\n\n while (hasNextPage) {\n const query = `\n query($org: String!, $cursor: String) {\n organization(login: $org) {\n teams(first: 100, after: $cursor) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n id\n databaseId\n slug\n name\n members {\n totalCount\n }\n }\n }\n }\n }\n `;\n\n const variables = {\n org: this.copilotConfig.organization,\n cursor,\n };\n\n const response: any = await octokit.graphql(query, variables);\n const teamsData = response.organization.teams;\n\n // Filter teams with 5 or more members\n const filteredTeams = teamsData.nodes\n .filter((team: any) => team.members.totalCount >= 5)\n .map((team: any) => ({\n id: team.databaseId,\n slug: team.slug,\n name: team.name,\n }));\n\n teams.push(...filteredTeams);\n\n hasNextPage = teamsData.pageInfo.hasNextPage;\n cursor = teamsData.pageInfo.endCursor;\n }\n\n return teams;\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":";;;;;;AA6CO,MAAM,YAAkC,CAAA;AAAA,EAI7C,WAAA,CACmB,eACA,MACjB,EAAA;AAFiB,IAAA,IAAA,CAAA,aAAA,GAAA,aAAA;AACA,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA;AAChB,EANK,iBAAA;AAAA,EACA,mBAAA;AAAA,EAOR,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,MAAc,WACZ,IACkB,EAAA;AAClB,IAAM,MAAA,WAAA,GAAc,MAAM,IAAA,CAAK,cAAe,EAAA;AAC9C,IAAA,MAAM,OAAU,GAAA,WAAA,CAAY,IAAI,CAAA,EAAG,WAAW,EAAC;AAE/C,IAAA,OAAO,IAAIC,YAAQ,CAAA;AAAA,MACjB,OAAA,EAAS,KAAK,aAAc,CAAA,UAAA;AAAA,MAC5B,MAAM,OAAQ,CAAA,aAAA,EAAe,OAAQ,CAAA,SAAA,EAAW,EAAE,CAAK,IAAA,EAAA;AAAA,MACvD,OAAS,EAAA;AAAA,QACP,MAAQ,EAAA,6BAAA;AAAA,QACR,sBAAwB,EAAA;AAAA;AAC1B,KACD,CAAA;AAAA;AACH,EAEA,MAAc,oBAAyC,GAAA;AACrD,IAAI,IAAA,CAAC,KAAK,iBAAmB,EAAA;AAC3B,MAAA,IAAA,CAAK,iBAAoB,GAAA,MAAM,IAAK,CAAA,UAAA,CAAW,YAAY,CAAA;AAAA;AAE7D,IAAA,OAAO,IAAK,CAAA,iBAAA;AAAA;AACd,EAEA,MAAc,sBAA2C,GAAA;AACvD,IAAI,IAAA,CAAC,KAAK,mBAAqB,EAAA;AAC7B,MAAA,IAAA,CAAK,mBAAsB,GAAA,MAAM,IAAK,CAAA,UAAA,CAAW,cAAc,CAAA;AAAA;AAEjE,IAAA,OAAO,IAAK,CAAA,mBAAA;AAAA;AACd,EAEA,MAAM,6BAA2D,GAAA;AAC/D,IAAM,MAAA,OAAA,GAAU,MAAM,IAAA,CAAK,oBAAqB,EAAA;AAChD,IAAA,MAAM,IAAO,GAAA,CAAA,aAAA,EAAgB,IAAK,CAAA,aAAA,CAAc,UAAU,CAAA,gBAAA,CAAA;AAE1D,IAAI,IAAA;AACF,MAAA,MAAM,WAAW,MAAM,OAAA,CAAQ,OAAQ,CAAA,CAAA,IAAA,EAAO,IAAI,CAAE,CAAA,CAAA;AACpD,MAAA,OAAO,QAAS,CAAA,IAAA;AAAA,aACT,KAAO,EAAA;AACd,MAAA,MAAMC,oBAAc,CAAA,YAAA,CAAa,KAAM,CAAA,QAAA,IAAY,KAAK,CAAA;AAAA;AAC1D;AACF,EAEA,MAAM,kCACJ,MAC2B,EAAA;AAC3B,IAAM,MAAA,OAAA,GAAU,MAAM,IAAA,CAAK,oBAAqB,EAAA;AAChD,IAAA,MAAM,OAAO,CAAgB,aAAA,EAAA,IAAA,CAAK,aAAc,CAAA,UAAU,SAAS,MAAM,CAAA,gBAAA,CAAA;AAEzE,IAAI,IAAA;AACF,MAAA,MAAM,WAAW,MAAM,OAAA,CAAQ,OAAQ,CAAA,CAAA,IAAA,EAAO,IAAI,CAAE,CAAA,CAAA;AACpD,MAAA,OAAO,QAAS,CAAA,IAAA;AAAA,aACT,KAAO,EAAA;AACd,MAAA,MAAMA,oBAAc,CAAA,YAAA,CAAa,KAAM,CAAA,QAAA,IAAY,KAAK,CAAA;AAAA;AAC1D;AACF,EAEA,MAAM,oBAA4C,GAAA;AAChD,IAAM,MAAA,OAAA,GAAU,MAAM,IAAA,CAAK,oBAAqB,EAAA;AAEhD,IAAI,IAAA;AACF,MAAA,MAAM,QAAoB,EAAC;AAC3B,MAAA,IAAI,MAAwB,GAAA,IAAA;AAC5B,MAAA,IAAI,WAAc,GAAA,IAAA;AAElB,MAAA,OAAO,WAAa,EAAA;AAClB,QAAA,MAAM,KAAQ,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAAA,CAAA;AA0Bd,QAAA,MAAM,SAAY,GAAA;AAAA,UAChB,UAAA,EAAY,KAAK,aAAc,CAAA,UAAA;AAAA,UAC/B;AAAA,SACF;AAEA,QAAA,MAAM,QAAgB,GAAA,MAAM,OAAQ,CAAA,OAAA,CAAQ,OAAO,SAAS,CAAA;AAG5D,QAAA,MAAM,QAAW,GAAA,QAAA,CAAS,UAAW,CAAA,aAAA,CAAc,KAAM,CAAA,OAAA;AAAA,UACvD,CAAC,GAAa,KAAA,GAAA,CAAI,KAAM,CAAA;AAAA,SAC1B;AAGA,QAAA,MAAM,aAAgB,GAAA,QAAA,CACnB,MAAO,CAAA,CAAC,IAAc,KAAA,IAAA,CAAK,OAAQ,CAAA,UAAA,IAAc,CAAC,CAAA,CAClD,GAAI,CAAA,CAAC,IAAe,MAAA;AAAA,UACnB,IAAI,IAAK,CAAA,UAAA;AAAA,UACT,MAAM,IAAK,CAAA,IAAA;AAAA,UACX,MAAM,IAAK,CAAA;AAAA,SACX,CAAA,CAAA;AAEJ,QAAM,KAAA,CAAA,IAAA,CAAK,GAAG,aAAa,CAAA;AAG3B,QAAc,WAAA,GAAA,QAAA,CAAS,UAAW,CAAA,aAAA,CAAc,KAAM,CAAA,IAAA;AAAA,UACpD,CAAC,GAAA,KAAa,GAAI,CAAA,KAAA,CAAM,QAAS,CAAA;AAAA,SACnC;AAEA,QAAA,IAAI,WAAa,EAAA;AACf,UAAS,MAAA,GAAA,QAAA,CAAS,UAAW,CAAA,aAAA,CAAc,KAAM,CAAA,IAAA;AAAA,YAC/C,CAAC,GAAA,KAAa,GAAI,CAAA,KAAA,CAAM,QAAS,CAAA;AAAA,WACnC,EAAG,MAAM,QAAS,CAAA,SAAA;AAAA;AACpB;AAGF,MAAO,OAAA,KAAA;AAAA,aACA,KAAO,EAAA;AACd,MAAA,MAAMA,oBAAc,CAAA,YAAA,CAAa,KAAM,CAAA,QAAA,IAAY,KAAK,CAAA;AAAA;AAC1D;AACF,EAEA,MAAM,oBAAqC,GAAA;AACzC,IAAM,MAAA,OAAA,GAAU,MAAM,IAAA,CAAK,oBAAqB,EAAA;AAChD,IAAA,MAAM,IAAO,GAAA,CAAA,aAAA,EAAgB,IAAK,CAAA,aAAA,CAAc,UAAU,CAAA,sBAAA,CAAA;AAE1D,IAAI,IAAA;AACF,MAAA,MAAM,QAAQ,MAAM,OAAA,CAAQ,QAAS,CAAA,CAAA,IAAA,EAAO,IAAI,CAAI,CAAA,EAAA;AAAA,QAClD,QAAU,EAAA;AAAA;AAAA,OACX,CAAA;AAED,MAAO,OAAA,IAAA,CAAK,sBAAsB,KAAuB,CAAA;AAAA,aAClD,KAAO,EAAA;AACd,MAAA,MAAMA,oBAAc,CAAA,YAAA,CAAa,KAAM,CAAA,QAAA,IAAY,KAAK,CAAA;AAAA;AAC1D;AACF,EAEA,MAAM,+BAA6D,GAAA;AACjE,IAAM,MAAA,OAAA,GAAU,MAAM,IAAA,CAAK,sBAAuB,EAAA;AAClD,IAAA,MAAM,IAAO,GAAA,CAAA,MAAA,EAAS,IAAK,CAAA,aAAA,CAAc,YAAY,CAAA,gBAAA,CAAA;AAErD,IAAI,IAAA;AACF,MAAA,MAAM,WAAW,MAAM,OAAA,CAAQ,OAAQ,CAAA,CAAA,IAAA,EAAO,IAAI,CAAE,CAAA,CAAA;AACpD,MAAA,OAAO,QAAS,CAAA,IAAA;AAAA,aACT,KAAO,EAAA;AACd,MAAA,MAAMA,oBAAc,CAAA,YAAA,CAAa,KAAM,CAAA,QAAA,IAAY,KAAK,CAAA;AAAA;AAC1D;AACF,EAEA,MAAM,oCACJ,MAC2B,EAAA;AAC3B,IAAM,MAAA,OAAA,GAAU,MAAM,IAAA,CAAK,sBAAuB,EAAA;AAClD,IAAA,MAAM,OAAO,CAAS,MAAA,EAAA,IAAA,CAAK,aAAc,CAAA,YAAY,SAAS,MAAM,CAAA,gBAAA,CAAA;AAEpE,IAAI,IAAA;AACF,MAAA,MAAM,WAAW,MAAM,OAAA,CAAQ,OAAQ,CAAA,CAAA,IAAA,EAAO,IAAI,CAAE,CAAA,CAAA;AACpD,MAAA,OAAO,QAAS,CAAA,IAAA;AAAA,aACT,KAAO,EAAA;AACd,MAAA,MAAMA,oBAAc,CAAA,YAAA,CAAa,KAAM,CAAA,QAAA,IAAY,KAAK,CAAA;AAAA;AAC1D;AACF,EAEA,MAAM,sBAA8C,GAAA;AAClD,IAAM,MAAA,OAAA,GAAU,MAAM,IAAA,CAAK,sBAAuB,EAAA;AAElD,IAAI,IAAA;AACF,MAAA,MAAM,QAAoB,EAAC;AAC3B,MAAA,IAAI,MAAwB,GAAA,IAAA;AAC5B,MAAA,IAAI,WAAc,GAAA,IAAA;AAElB,MAAA,OAAO,WAAa,EAAA;AAClB,QAAA,MAAM,KAAQ,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAAA,CAAA;AAsBd,QAAA,MAAM,SAAY,GAAA;AAAA,UAChB,GAAA,EAAK,KAAK,aAAc,CAAA,YAAA;AAAA,UACxB;AAAA,SACF;AAEA,QAAA,MAAM,QAAgB,GAAA,MAAM,OAAQ,CAAA,OAAA,CAAQ,OAAO,SAAS,CAAA;AAC5D,QAAM,MAAA,SAAA,GAAY,SAAS,YAAa,CAAA,KAAA;AAGxC,QAAA,MAAM,aAAgB,GAAA,SAAA,CAAU,KAC7B,CAAA,MAAA,CAAO,CAAC,IAAA,KAAc,IAAK,CAAA,OAAA,CAAQ,UAAc,IAAA,CAAC,CAClD,CAAA,GAAA,CAAI,CAAC,IAAe,MAAA;AAAA,UACnB,IAAI,IAAK,CAAA,UAAA;AAAA,UACT,MAAM,IAAK,CAAA,IAAA;AAAA,UACX,MAAM,IAAK,CAAA;AAAA,SACX,CAAA,CAAA;AAEJ,QAAM,KAAA,CAAA,IAAA,CAAK,GAAG,aAAa,CAAA;AAE3B,QAAA,WAAA,GAAc,UAAU,QAAS,CAAA,WAAA;AACjC,QAAA,MAAA,GAAS,UAAU,QAAS,CAAA,SAAA;AAAA;AAG9B,MAAO,OAAA,KAAA;AAAA,aACA,KAAO,EAAA;AACd,MAAA,MAAMA,oBAAc,CAAA,YAAA,CAAa,KAAM,CAAA,QAAA,IAAY,KAAK,CAAA;AAAA;AAC1D;AACF,EAEA,MAAM,sBAAgD,GAAA;AACpD,IAAM,MAAA,OAAA,GAAU,MAAM,IAAA,CAAK,sBAAuB,EAAA;AAElD,IAAI,IAAA;AACF,MAAA,MAAM,QAAQ,MAAM,OAAA,CAAQ,QAAS,CAAA,OAAA,CAAQ,QAAQ,gBAAkB,EAAA;AAAA,QACrE,GAAA,EAAK,KAAK,aAAc,CAAA,YAAA;AAAA,QACxB,QAAU,EAAA;AAAA;AAAA,OACX,CAAA;AAED,MAAO,OAAA,IAAA,CAAK,sBAAsB,KAAuB,CAAA;AAAA,aAClD,KAAO,EAAA;AACd,MAAA,MAAMA,oBAAc,CAAA,YAAA,CAAa,KAAM,CAAA,QAAA,IAAY,KAAK,CAAA;AAAA;AAC1D;AACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,sBAAsB,IAAoC,EAAA;AACxD,IAAI,IAAA,IAAA,CAAK,WAAW,CAAG,EAAA;AACrB,MAAO,OAAA;AAAA,QACL,WAAa,EAAA,CAAA;AAAA,QACb,OAAO;AAAC,OACV;AAAA;AAKF,IAAM,MAAA,UAAA,GAAa,IAAK,CAAA,CAAC,CAAE,CAAA,WAAA;AAC3B,IAAA,MAAM,QAAQ,IAAK,CAAA,GAAA,CAAI,UAAQ,IAAK,CAAA,KAAK,EAAE,IAAK,EAAA;AAEhD,IAAO,OAAA;AAAA,MACL,WAAa,EAAA,UAAA;AAAA,MACb;AAAA,KACF;AAAA;AAEJ;;;;"}
@@ -21,6 +21,10 @@ async function discoverEnterpriseTeamMetrics({
21
21
  logger.info(
22
22
  `[discoverEnterpriseTeamMetrics] Fetched ${teams.length} teams`
23
23
  );
24
+ const seats = await api.fetchEnterpriseSeats();
25
+ logger.info(
26
+ `[discoverEnterpriseTeamMetrics] Fetched ${seats.length} seats from enterprise`
27
+ );
24
28
  for (const team of teams) {
25
29
  try {
26
30
  logger.info(
@@ -109,7 +113,6 @@ async function discoverEnterpriseTeamMetrics({
109
113
  await batchInsert.batchInsertInChunks(ideChatEditorModels, 30, async (chunk) => {
110
114
  await db.batchInsertIdeChatEditorModels(chunk);
111
115
  });
112
- const seats = await api.fetchEnterpriseSeats();
113
116
  const seatsToInsert = metricHelpers.convertToTeamSeatAnalysis(
114
117
  seats,
115
118
  type,
@@ -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 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}`,\n error,\n );\n }\n }\n } catch (error) {\n logger.error('[discoverEnterpriseTeamMetrics] Error fetching teams', error);\n throw error;\n }\n}\n"],"names":["filterNewMetricsV2","filterBaseMetrics","filterIdeCompletionMetrics","filterIdeCompletionLanguageMetrics","filterIdeCompletionEditorMetrics","filterIdeCompletionEditorModelMetrics","filterIdeCompletionEditorModelLanguageMetrics","filterIdeChatMetrics","filterIdeEditorMetrics","filterIdeChatEditorModelMetrics","batchInsertInChunks","convertToTeamSeatAnalysis"],"mappings":"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"}
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 // Fetch seat analysis\n const seats = await api.fetchEnterpriseSeats();\n logger.info(\n `[discoverEnterpriseTeamMetrics] Fetched ${seats.length} seats from enterprise`,\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 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}`,\n error,\n );\n }\n }\n } catch (error) {\n logger.error('[discoverEnterpriseTeamMetrics] Error fetching teams', error);\n throw error;\n }\n}\n"],"names":["filterNewMetricsV2","filterBaseMetrics","filterIdeCompletionMetrics","filterIdeCompletionLanguageMetrics","filterIdeCompletionEditorMetrics","filterIdeCompletionEditorModelMetrics","filterIdeCompletionEditorModelLanguageMetrics","filterIdeChatMetrics","filterIdeEditorMetrics","filterIdeChatEditorModelMetrics","batchInsertInChunks","convertToTeamSeatAnalysis"],"mappings":"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"}
@@ -21,6 +21,10 @@ async function discoverOrganizationTeamMetrics({
21
21
  logger.info(
22
22
  `[discoverOrganizationTeamMetrics] Fetched ${teams.length} teams`
23
23
  );
24
+ const seats = await api.fetchOrganizationSeats();
25
+ logger.info(
26
+ `[discoverOrganizationTeamMetrics] Fetched ${seats.seats.length} seats from organization`
27
+ );
24
28
  for (const team of teams) {
25
29
  try {
26
30
  logger.info(
@@ -109,7 +113,6 @@ async function discoverOrganizationTeamMetrics({
109
113
  await batchInsert.batchInsertInChunks(ideChatEditorModels, 30, async (chunk) => {
110
114
  await db.batchInsertIdeChatEditorModels(chunk);
111
115
  });
112
- const seats = await api.fetchOrganizationSeats();
113
116
  const seatsToInsert = metricHelpers.convertToTeamSeatAnalysis(
114
117
  seats,
115
118
  type,
@@ -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 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}`,\n error,\n );\n }\n }\n } catch (error) {\n logger.error(\n '[discoverOrganizationTeamMetrics] Error fetching teams',\n 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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"}
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 // Fetch seat analysis\n const seats = await api.fetchOrganizationSeats();\n logger.info(\n `[discoverOrganizationTeamMetrics] Fetched ${seats.seats.length} seats from organization`,\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 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}`,\n error,\n );\n }\n }\n } catch (error) {\n logger.error(\n '[discoverOrganizationTeamMetrics] Error fetching teams',\n 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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage-community/plugin-copilot-backend",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "homepage": "https://backstage.io",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/index.cjs.js",
@@ -42,13 +42,13 @@
42
42
  "postpack": "backstage-cli package postpack"
43
43
  },
44
44
  "dependencies": {
45
- "@backstage-community/plugin-copilot-common": "^0.12.0",
46
- "@backstage/backend-app-api": "^1.2.6",
47
- "@backstage/backend-defaults": "^0.12.0",
48
- "@backstage/backend-plugin-api": "^1.4.2",
45
+ "@backstage-community/plugin-copilot-common": "^0.13.0",
46
+ "@backstage/backend-app-api": "^1.2.7",
47
+ "@backstage/backend-defaults": "^0.12.1",
48
+ "@backstage/backend-plugin-api": "^1.4.3",
49
49
  "@backstage/config": "^1.3.3",
50
50
  "@backstage/errors": "^1.2.7",
51
- "@backstage/integration": "^1.17.1",
51
+ "@backstage/integration": "^1.18.0",
52
52
  "@octokit/rest": "20.1.2",
53
53
  "@types/express": "*",
54
54
  "express": "^4.17.1",
@@ -61,10 +61,10 @@
61
61
  "zod": "^3.23.8"
62
62
  },
63
63
  "devDependencies": {
64
- "@backstage/backend-test-utils": "^1.8.0",
65
- "@backstage/cli": "^0.34.0",
66
- "@backstage/plugin-auth-backend": "^0.25.3",
67
- "@backstage/plugin-auth-backend-module-guest-provider": "^0.2.11",
64
+ "@backstage/backend-test-utils": "^1.9.0",
65
+ "@backstage/cli": "^0.34.3",
66
+ "@backstage/plugin-auth-backend": "^0.25.4",
67
+ "@backstage/plugin-auth-backend-module-guest-provider": "^0.2.12",
68
68
  "@backstage/test-utils": "^1.7.11",
69
69
  "@types/node-fetch": "^2.6.11",
70
70
  "@types/supertest": "^2.0.8",