feat: Created PullRequestsDashboardProvider for resolving team and team member relations

Signed-off-by: Marley Powell <Marley.Powell@exclaimer.com>
This commit is contained in:
Marley Powell
2021-12-16 14:35:21 +00:00
parent 9c2e6bb86a
commit 060a39e4d8
7 changed files with 191 additions and 19 deletions
@@ -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<string[] | undefined> {
public async getTeamMembers(team: Team): Promise<TeamMember[] | undefined> {
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(
@@ -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<string, Team>();
private teamMembers = new Map<string, TeamMember>();
private constructor(
private readonly logger: Logger,
private readonly azureDevOpsApi: AzureDevOpsApi,
) {}
public static async create(
logger: Logger,
azureDevOpsApi: AzureDevOpsApi,
): Promise<PullRequestsDashboardProvider> {
const provider = new PullRequestsDashboardProvider(logger, azureDevOpsApi);
await provider.readTeams();
return provider;
}
public async readTeams(): Promise<void> {
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<string, Team>();
this.teamMembers = new Map<string, TeamMember>();
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<DashboardPullRequest[]> {
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<Team[]> {
if (!this.teams.size) {
await this.readTeams();
}
return Array.from(this.teams.values());
}
public getTeamMembers(): TeamMember[] {
return Array.from(this.teamMembers.values());
}
}
@@ -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;
}