From 060a39e4d8701bd9d3d7132e108bc2bf5b896656 Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Thu, 16 Dec 2021 14:35:21 +0000 Subject: [PATCH 1/9] feat: Created `PullRequestsDashboardProvider` for resolving team and team member relations Signed-off-by: Marley Powell --- .../src/api/AzureDevOpsApi.ts | 30 ++-- .../src/api/PullRequestsDashboardProvider.ts | 129 ++++++++++++++++++ .../src/service/router.ts | 14 +- plugins/azure-devops-common/api-report.md | 20 ++- plugins/azure-devops-common/src/types.ts | 11 +- .../azure-devops/src/api/AzureDevOpsApi.ts | 2 + .../azure-devops/src/api/AzureDevOpsClient.ts | 4 + 7 files changed, 191 insertions(+), 19 deletions(-) create mode 100644 plugins/azure-devops-backend/src/api/PullRequestsDashboardProvider.ts diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts index 6e45a5f4e5..d82673beef 100644 --- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts +++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts @@ -28,6 +28,7 @@ import { PullRequestOptions, RepoBuild, Team, + TeamMember, } from '@backstage/plugin-azure-devops-common'; import { GitPullRequest, @@ -40,9 +41,9 @@ import { getArtifactId, } from '../utils'; +import { TeamMember as AdoTeamMember } from 'azure-devops-node-api/interfaces/common/VSSInterfaces'; import { Logger } from 'winston'; import { PolicyEvaluationRecord } from 'azure-devops-node-api/interfaces/PolicyInterfaces'; -import { TeamMember } from 'azure-devops-node-api/interfaces/common/VSSInterfaces'; import { WebApi } from 'azure-devops-node-api'; import { WebApiTeam } from 'azure-devops-node-api/interfaces/CoreInterfaces'; @@ -228,22 +229,19 @@ export class AzureDevOpsApi { const client = await this.webApi.getCoreApi(); const webApiTeams: WebApiTeam[] = await client.getAllTeams(); - const teams: Team[] = await Promise.all( - webApiTeams.map(async team => ({ - id: team.id, - name: team.name, - memberIds: await this.getTeamMemberIds(team), - })), - ); + const teams: Team[] = webApiTeams.map(team => ({ + id: team.id, + name: team.name, + projectId: team.projectId, + projectName: team.projectName, + })); return teams.sort((a, b) => a.name && b.name ? a.name.localeCompare(b.name) : 0, ); } - private async getTeamMemberIds( - team: WebApiTeam, - ): Promise { + public async getTeamMembers(team: Team): Promise { this.logger?.debug(`Getting team member ids for team '${team.name}'.`); if (!team.projectId || !team.id) { @@ -252,15 +250,17 @@ export class AzureDevOpsApi { const client = await this.webApi.getCoreApi(); - const teamMembers: TeamMember[] = + const teamMembers: AdoTeamMember[] = await client.getTeamMembersWithExtendedProperties( team.projectId, team.id, ); - return teamMembers - .map(teamMember => teamMember.identity?.id) - .filter((id): id is string => Boolean(id)); + return teamMembers.map(teamMember => ({ + id: teamMember.identity?.id, + displayName: teamMember.identity?.displayName, + uniqueName: teamMember.identity?.uniqueName, + })); } public async getBuildDefinitions( diff --git a/plugins/azure-devops-backend/src/api/PullRequestsDashboardProvider.ts b/plugins/azure-devops-backend/src/api/PullRequestsDashboardProvider.ts new file mode 100644 index 0000000000..9bb53637f7 --- /dev/null +++ b/plugins/azure-devops-backend/src/api/PullRequestsDashboardProvider.ts @@ -0,0 +1,129 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + DashboardPullRequest, + PullRequestOptions, + Team, + TeamMember, +} from '@backstage/plugin-azure-devops-common'; + +import { AzureDevOpsApi } from './AzureDevOpsApi'; +import { Logger } from 'winston'; + +export class PullRequestsDashboardProvider { + private teams = new Map(); + + private teamMembers = new Map(); + + private constructor( + private readonly logger: Logger, + private readonly azureDevOpsApi: AzureDevOpsApi, + ) {} + + public static async create( + logger: Logger, + azureDevOpsApi: AzureDevOpsApi, + ): Promise { + const provider = new PullRequestsDashboardProvider(logger, azureDevOpsApi); + await provider.readTeams(); + return provider; + } + + public async readTeams(): Promise { + this.logger.info('Reading teams.'); + + let teams = await this.azureDevOpsApi.getAllTeams(); + + // This is used to filter out the default Azure Devops project teams. + teams = teams.filter(team => + team.name && team.projectName + ? team.name !== `${team.projectName} Team` + : true, + ); + + this.teams = new Map(); + this.teamMembers = new Map(); + + await Promise.all( + teams.map(async team => { + const teamId = team.id; + + if (teamId) { + const teamMembers = await this.azureDevOpsApi.getTeamMembers(team); + + if (teamMembers) { + team.members = teamMembers.reduce((arr, teamMember) => { + const teamMemberId = teamMember.id; + + if (teamMemberId) { + arr.push(teamMemberId); + const memberOf = [ + ...(this.teamMembers.get(teamMemberId)?.memberOf ?? []), + teamId, + ]; + this.teamMembers.set(teamMemberId, { ...teamMember, memberOf }); + } + + return arr; + }, [] as string[]); + + this.teams.set(teamId, team); + } + } + }), + ); + } + + public async getDashboardPullRequests( + projectName: string, + options: PullRequestOptions, + ): Promise { + const dashboardPullRequests = + await this.azureDevOpsApi.getDashboardPullRequests(projectName, options); + + return dashboardPullRequests.map(pr => { + if (pr.createdBy?.id) { + const teamIds = this.teamMembers.get(pr.createdBy.id)?.memberOf; + pr.createdBy.teamIds = teamIds; + pr.createdBy.teamNames = teamIds?.map( + teamId => this.teams.get(teamId)?.name ?? '', + ); + } + + return pr; + }); + } + + public getUserTeamIds(email: string): string[] { + return ( + this.getTeamMembers().find(teamMember => teamMember.uniqueName === email) + ?.memberOf ?? [] + ); + } + + public async getAllTeams(): Promise { + if (!this.teams.size) { + await this.readTeams(); + } + + return Array.from(this.teams.values()); + } + + public getTeamMembers(): TeamMember[] { + return Array.from(this.teamMembers.values()); + } +} diff --git a/plugins/azure-devops-backend/src/service/router.ts b/plugins/azure-devops-backend/src/service/router.ts index 99c140a424..6482ed2f59 100644 --- a/plugins/azure-devops-backend/src/service/router.ts +++ b/plugins/azure-devops-backend/src/service/router.ts @@ -24,6 +24,7 @@ import { WebApi, getPersonalAccessTokenHandler } from 'azure-devops-node-api'; import { AzureDevOpsApi } from '../api'; import { Config } from '@backstage/config'; import { Logger } from 'winston'; +import { PullRequestsDashboardProvider } from '../api/PullRequestsDashboardProvider'; import Router from 'express-promise-router'; import { errorHandler } from '@backstage/backend-common'; import express from 'express'; @@ -52,6 +53,9 @@ export async function createRouter( const azureDevOpsApi = options.azureDevOpsApi || new AzureDevOpsApi(logger, webApi); + const pullRequestsDashboardProvider = + await PullRequestsDashboardProvider.create(logger, azureDevOpsApi); + const router = Router(); router.use(express.json()); @@ -131,7 +135,7 @@ export async function createRouter( }; const pullRequests: DashboardPullRequest[] = - await azureDevOpsApi.getDashboardPullRequests( + await pullRequestsDashboardProvider.getDashboardPullRequests( projectName, pullRequestOptions, ); @@ -140,7 +144,7 @@ export async function createRouter( }); router.get('/all-teams', async (_req, res) => { - const allTeams = await azureDevOpsApi.getAllTeams(); + const allTeams = await pullRequestsDashboardProvider.getAllTeams(); res.status(200).json(allTeams); }); @@ -170,6 +174,12 @@ export async function createRouter( res.status(200).json(builds); }); + router.get('/users/:userId/team-ids', async (req, res) => { + const { userId } = req.params; + const teamIds = pullRequestsDashboardProvider.getUserTeamIds(userId); + res.status(200).json(teamIds); + }); + router.use(errorHandler()); return router; } diff --git a/plugins/azure-devops-common/api-report.md b/plugins/azure-devops-common/api-report.md index 85a9e54847..308fd54c61 100644 --- a/plugins/azure-devops-common/api-report.md +++ b/plugins/azure-devops-common/api-report.md @@ -270,9 +270,27 @@ export interface Team { // (undocumented) id?: string; // (undocumented) - memberIds?: string[]; + members?: string[]; // (undocumented) name?: string; + // (undocumented) + projectId?: string; + // (undocumented) + projectName?: string; +} + +// Warning: (ae-missing-release-tag) "TeamMember" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface TeamMember { + // (undocumented) + displayName?: string; + // (undocumented) + id?: string; + // (undocumented) + memberOf?: string[]; + // (undocumented) + uniqueName?: string; } // (No @packageDocumentation comment for this package) diff --git a/plugins/azure-devops-common/src/types.ts b/plugins/azure-devops-common/src/types.ts index 0befcf0b60..7978e3326a 100644 --- a/plugins/azure-devops-common/src/types.ts +++ b/plugins/azure-devops-common/src/types.ts @@ -178,7 +178,16 @@ export interface Repository { export interface Team { id?: string; name?: string; - memberIds?: string[]; + projectId?: string; + projectName?: string; + members?: string[]; +} + +export interface TeamMember { + id?: string; + displayName?: string; + uniqueName?: string; + memberOf?: string[]; } /** diff --git a/plugins/azure-devops/src/api/AzureDevOpsApi.ts b/plugins/azure-devops/src/api/AzureDevOpsApi.ts index 80d36f0adf..d87986c7f7 100644 --- a/plugins/azure-devops/src/api/AzureDevOpsApi.ts +++ b/plugins/azure-devops/src/api/AzureDevOpsApi.ts @@ -49,4 +49,6 @@ export interface AzureDevOpsApi { ): Promise; getAllTeams(): Promise; + + getUserTeamIds(userId: string): Promise; } diff --git a/plugins/azure-devops/src/api/AzureDevOpsClient.ts b/plugins/azure-devops/src/api/AzureDevOpsClient.ts index 28d7e19cff..42c5ac3f6c 100644 --- a/plugins/azure-devops/src/api/AzureDevOpsClient.ts +++ b/plugins/azure-devops/src/api/AzureDevOpsClient.ts @@ -88,6 +88,10 @@ export class AzureDevOpsClient implements AzureDevOpsApi { return this.get('all-teams'); } + public getUserTeamIds(userId: string): Promise { + return this.get(`users/${userId}/team-ids`); + } + private async get(path: string): Promise { const baseUrl = `${await this.discoveryApi.getBaseUrl('azure-devops')}/`; const url = new URL(path, baseUrl); From 562670a7cbf9c1ab451f66c1ebf088d282c45d0e Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Thu, 16 Dec 2021 14:38:13 +0000 Subject: [PATCH 2/9] feat: Created `useUserTeamIds` hook. Signed-off-by: Marley Powell --- plugins/azure-devops/src/hooks/index.ts | 1 + .../azure-devops/src/hooks/useUserTeamIds.ts | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 plugins/azure-devops/src/hooks/useUserTeamIds.ts diff --git a/plugins/azure-devops/src/hooks/index.ts b/plugins/azure-devops/src/hooks/index.ts index 44b94f3f4b..a3864c21f7 100644 --- a/plugins/azure-devops/src/hooks/index.ts +++ b/plugins/azure-devops/src/hooks/index.ts @@ -20,3 +20,4 @@ export * from './useProjectRepoFromEntity'; export * from './usePullRequests'; export * from './useRepoBuilds'; export * from './useUserEmail'; +export * from './useUserTeamIds'; diff --git a/plugins/azure-devops/src/hooks/useUserTeamIds.ts b/plugins/azure-devops/src/hooks/useUserTeamIds.ts new file mode 100644 index 0000000000..878dbebdfe --- /dev/null +++ b/plugins/azure-devops/src/hooks/useUserTeamIds.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { azureDevOpsApiRef } from '../api'; +import { useApi } from '@backstage/core-plugin-api'; +import { useAsync } from 'react-use'; + +export function useUserTeamIds(userId: string | undefined): { + teamIds?: string[]; + loading: boolean; + error?: Error; +} { + const api = useApi(azureDevOpsApiRef); + + const { + value: teamIds, + loading, + error, + } = useAsync(() => { + return userId ? api.getUserTeamIds(userId) : Promise.resolve(undefined); + }, [api]); + + return { + teamIds, + loading, + error, + }; +} From da8f2d2091633a3376f9b4a25f536c58db403a2c Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Thu, 16 Dec 2021 14:39:26 +0000 Subject: [PATCH 3/9] feat: Updated `useFilterProcessor` to provide teamIds for `AssignedToCurrentUsersTeams` and `CreatedByCurrentUsersTeams` filters. Signed-off-by: Marley Powell --- .../PullRequestsPage/lib/hooks/useFilterProcessor.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/hooks/useFilterProcessor.ts b/plugins/azure-devops/src/components/PullRequestsPage/lib/hooks/useFilterProcessor.ts index 546b23430d..1e31b3b3ce 100644 --- a/plugins/azure-devops/src/components/PullRequestsPage/lib/hooks/useFilterProcessor.ts +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/hooks/useFilterProcessor.ts @@ -15,11 +15,11 @@ */ import { Filter, FilterType } from '../filters'; - -import { useUserEmail } from '../../../../hooks'; +import { useUserEmail, useUserTeamIds } from '../../../../hooks'; export function useFilterProcessor(): (filters: Filter[]) => Filter[] { const userEmail = useUserEmail(); + const { teamIds } = useUserTeamIds(userEmail); return (filters: Filter[]): Filter[] => { for (const filter of filters) { @@ -29,6 +29,11 @@ export function useFilterProcessor(): (filters: Filter[]) => Filter[] { filter.email = userEmail; break; + case FilterType.AssignedToCurrentUsersTeams: + case FilterType.CreatedByCurrentUsersTeams: + filter.teamIds = teamIds; + break; + default: break; } From 7d84a9aafb1d543e559f2c747671bad4a8b4a589 Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Thu, 16 Dec 2021 14:47:41 +0000 Subject: [PATCH 4/9] chore: Updated API reports. Signed-off-by: Marley Powell --- plugins/azure-devops-backend/api-report.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/azure-devops-backend/api-report.md b/plugins/azure-devops-backend/api-report.md index 9574215e3e..f28f6f9c00 100644 --- a/plugins/azure-devops-backend/api-report.md +++ b/plugins/azure-devops-backend/api-report.md @@ -15,6 +15,7 @@ import { PullRequest } from '@backstage/plugin-azure-devops-common'; import { PullRequestOptions } from '@backstage/plugin-azure-devops-common'; import { RepoBuild } from '@backstage/plugin-azure-devops-common'; import { Team } from '@backstage/plugin-azure-devops-common'; +import { TeamMember } from '@backstage/plugin-azure-devops-common'; import { WebApi } from 'azure-devops-node-api'; // Warning: (ae-missing-release-tag) "AzureDevOpsApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -71,6 +72,8 @@ export class AzureDevOpsApi { repoName: string, top: number, ): Promise; + // (undocumented) + getTeamMembers(team: Team): Promise; } // Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) From a2ed2c2d69c42de42a547abd745629be70ee2bf4 Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Thu, 16 Dec 2021 14:50:03 +0000 Subject: [PATCH 5/9] chore: Generated changeset Signed-off-by: Marley Powell --- .changeset/olive-laws-thank.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .changeset/olive-laws-thank.md diff --git a/.changeset/olive-laws-thank.md b/.changeset/olive-laws-thank.md new file mode 100644 index 0000000000..9442e95a43 --- /dev/null +++ b/.changeset/olive-laws-thank.md @@ -0,0 +1,9 @@ +--- +'@backstage/plugin-azure-devops-backend': minor +'@backstage/plugin-azure-devops': patch +'@backstage/plugin-azure-devops-common': patch +--- + +- feat: Created PullRequestsDashboardProvider for resolving team and team member relations +- feat: Created useUserTeamIds hook. +- feat: Updated useFilterProcessor to provide teamIds for `AssignedToCurrentUsersTeams` and `CreatedByCurrentUsersTeams` filters. From 17da4e34c3d5edc0a8d15560bdb4678114c45bcd Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Thu, 16 Dec 2021 14:53:08 +0000 Subject: [PATCH 6/9] chore: Updated changeset Signed-off-by: Marley Powell --- .changeset/olive-laws-thank.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/olive-laws-thank.md b/.changeset/olive-laws-thank.md index 9442e95a43..06a94d5614 100644 --- a/.changeset/olive-laws-thank.md +++ b/.changeset/olive-laws-thank.md @@ -1,7 +1,7 @@ --- '@backstage/plugin-azure-devops-backend': minor '@backstage/plugin-azure-devops': patch -'@backstage/plugin-azure-devops-common': patch +'@backstage/plugin-azure-devops-common': minor --- - feat: Created PullRequestsDashboardProvider for resolving team and team member relations From 30d3623bf16cbbe1fe94fef083f4ff6a43a1db0a Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Thu, 16 Dec 2021 15:24:54 +0000 Subject: [PATCH 7/9] test: Updated router unit tests. Signed-off-by: Marley Powell --- plugins/azure-devops-backend/src/service/router.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plugins/azure-devops-backend/src/service/router.test.ts b/plugins/azure-devops-backend/src/service/router.test.ts index 87135781e6..335e581d73 100644 --- a/plugins/azure-devops-backend/src/service/router.test.ts +++ b/plugins/azure-devops-backend/src/service/router.test.ts @@ -49,6 +49,8 @@ describe('createRouter', () => { getPullRequests: jest.fn(), getBuilds: jest.fn(), getBuildRuns: jest.fn(), + getAllTeams: jest.fn().mockReturnValue([]), + getTeamMembers: jest.fn(), } as any; const router = await createRouter({ azureDevOpsApi, @@ -406,4 +408,11 @@ describe('createRouter', () => { }); }); }); + + describe('GET /users/:userId/team-ids', () => { + it('fetches a a list of teams', async () => { + const response = await request(app).get('/users/user1/team-ids'); + expect(response.status).toEqual(200); + }); + }); }); From dc55aa89f64e3ff7839e54d5d8f8ade1cc42fef9 Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Tue, 4 Jan 2022 09:17:49 +0000 Subject: [PATCH 8/9] fix: Implemented a `p-limit` in `PullRequestsDashboardProvider` to stop unlimited parallelism. Signed-off-by: Marley Powell --- plugins/azure-devops-backend/package.json | 1 + .../src/api/PullRequestsDashboardProvider.ts | 48 +++++++++++-------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/plugins/azure-devops-backend/package.json b/plugins/azure-devops-backend/package.json index a7aec3f2d6..73604f63ef 100644 --- a/plugins/azure-devops-backend/package.json +++ b/plugins/azure-devops-backend/package.json @@ -27,6 +27,7 @@ "azure-devops-node-api": "^11.0.1", "express": "^4.17.1", "express-promise-router": "^4.1.0", + "p-limit": "^3.1.0", "winston": "^3.2.1", "yn": "^4.0.0" }, diff --git a/plugins/azure-devops-backend/src/api/PullRequestsDashboardProvider.ts b/plugins/azure-devops-backend/src/api/PullRequestsDashboardProvider.ts index 9bb53637f7..7ddfc0c7ff 100644 --- a/plugins/azure-devops-backend/src/api/PullRequestsDashboardProvider.ts +++ b/plugins/azure-devops-backend/src/api/PullRequestsDashboardProvider.ts @@ -23,6 +23,7 @@ import { import { AzureDevOpsApi } from './AzureDevOpsApi'; import { Logger } from 'winston'; +import limiterFactory from 'p-limit'; export class PullRequestsDashboardProvider { private teams = new Map(); @@ -58,33 +59,40 @@ export class PullRequestsDashboardProvider { this.teams = new Map(); this.teamMembers = new Map(); + const limiter = limiterFactory(5); + await Promise.all( - teams.map(async team => { - const teamId = team.id; + teams.map(team => + limiter(async () => { + const teamId = team.id; - if (teamId) { - const teamMembers = await this.azureDevOpsApi.getTeamMembers(team); + if (teamId) { + const teamMembers = await this.azureDevOpsApi.getTeamMembers(team); - if (teamMembers) { - team.members = teamMembers.reduce((arr, teamMember) => { - const teamMemberId = teamMember.id; + if (teamMembers) { + team.members = teamMembers.reduce((arr, teamMember) => { + const teamMemberId = teamMember.id; - if (teamMemberId) { - arr.push(teamMemberId); - const memberOf = [ - ...(this.teamMembers.get(teamMemberId)?.memberOf ?? []), - teamId, - ]; - this.teamMembers.set(teamMemberId, { ...teamMember, memberOf }); - } + if (teamMemberId) { + arr.push(teamMemberId); + const memberOf = [ + ...(this.teamMembers.get(teamMemberId)?.memberOf ?? []), + teamId, + ]; + this.teamMembers.set(teamMemberId, { + ...teamMember, + memberOf, + }); + } - return arr; - }, [] as string[]); + return arr; + }, [] as string[]); - this.teams.set(teamId, team); + this.teams.set(teamId, team); + } } - } - }), + }), + ), ); } From 7de84a823940434c03896d41a2da4aff16e85a3d Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Wed, 12 Jan 2022 08:59:16 +0000 Subject: [PATCH 9/9] refactor: Code review improvements around `getTeamMembers` parameters. Signed-off-by: Marley Powell --- plugins/azure-devops-backend/api-report.md | 8 +++++++- .../src/api/AzureDevOpsApi.ts | 19 +++++++++---------- .../src/api/PullRequestsDashboardProvider.ts | 10 +++++++++- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/plugins/azure-devops-backend/api-report.md b/plugins/azure-devops-backend/api-report.md index f28f6f9c00..579367e2dd 100644 --- a/plugins/azure-devops-backend/api-report.md +++ b/plugins/azure-devops-backend/api-report.md @@ -73,7 +73,13 @@ export class AzureDevOpsApi { top: number, ): Promise; // (undocumented) - getTeamMembers(team: Team): Promise; + getTeamMembers({ + projectId, + teamId, + }: { + projectId: string; + teamId: string; + }): Promise; } // Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts index d82673beef..ff63af183e 100644 --- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts +++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts @@ -241,20 +241,19 @@ export class AzureDevOpsApi { ); } - public async getTeamMembers(team: Team): Promise { - this.logger?.debug(`Getting team member ids for team '${team.name}'.`); - - if (!team.projectId || !team.id) { - return undefined; - } + public async getTeamMembers({ + projectId, + teamId, + }: { + projectId: string; + teamId: string; + }): Promise { + this.logger?.debug(`Getting team member ids for team '${teamId}'.`); const client = await this.webApi.getCoreApi(); const teamMembers: AdoTeamMember[] = - await client.getTeamMembersWithExtendedProperties( - team.projectId, - team.id, - ); + await client.getTeamMembersWithExtendedProperties(projectId, teamId); return teamMembers.map(teamMember => ({ id: teamMember.identity?.id, diff --git a/plugins/azure-devops-backend/src/api/PullRequestsDashboardProvider.ts b/plugins/azure-devops-backend/src/api/PullRequestsDashboardProvider.ts index 7ddfc0c7ff..a4879de7be 100644 --- a/plugins/azure-devops-backend/src/api/PullRequestsDashboardProvider.ts +++ b/plugins/azure-devops-backend/src/api/PullRequestsDashboardProvider.ts @@ -65,9 +65,17 @@ export class PullRequestsDashboardProvider { teams.map(team => limiter(async () => { const teamId = team.id; + const projectId = team.projectId; if (teamId) { - const teamMembers = await this.azureDevOpsApi.getTeamMembers(team); + let teamMembers: TeamMember[] | undefined; + + if (projectId) { + teamMembers = await this.azureDevOpsApi.getTeamMembers({ + projectId, + teamId, + }); + } if (teamMembers) { team.members = teamMembers.reduce((arr, teamMember) => {