Merge pull request #23634 from QuadmanSWE/patch-plugin-azure-devops-backstage-getAllTeams-topTeams
Add optional limit parameter for getAllTeams method, default 100
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
---
|
||||
'@backstage/plugin-azure-devops-backend': patch
|
||||
'@backstage/plugin-azure-devops': patch
|
||||
'@backstage/plugin-azure-common': patch
|
||||
---
|
||||
|
||||
`getAllTeams` now accepts an optional `limit` parameter which can be used to return more than the default limit of 100 teams from the Azure DevOps API
|
||||
|
||||
`pullRequestOptions` have been equipped with `teamsLimit` so that the property can be used with `getAllTeams`
|
||||
@@ -56,7 +56,7 @@ export class AzureDevOpsApi {
|
||||
},
|
||||
): AzureDevOpsApi;
|
||||
// (undocumented)
|
||||
getAllTeams(): Promise<Team[]>;
|
||||
getAllTeams(options?: { limit?: number }): Promise<Team[]>;
|
||||
// (undocumented)
|
||||
getBuildDefinitions(
|
||||
projectName: string,
|
||||
|
||||
@@ -397,12 +397,18 @@ export class AzureDevOpsApi {
|
||||
.filter((policy): policy is Policy => Boolean(policy));
|
||||
}
|
||||
|
||||
public async getAllTeams(): Promise<Team[]> {
|
||||
public async getAllTeams(options?: { limit?: number }): Promise<Team[]> {
|
||||
this.logger?.debug('Getting all teams.');
|
||||
|
||||
const webApi = await this.getWebApi();
|
||||
const client = await webApi.getCoreApi();
|
||||
const webApiTeams: WebApiTeam[] = await client.getAllTeams();
|
||||
|
||||
const webApiTeams: WebApiTeam[] = await client.getAllTeams(
|
||||
undefined,
|
||||
options?.limit,
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
|
||||
const teams: Team[] = webApiTeams.map(team => ({
|
||||
id: team.id,
|
||||
|
||||
@@ -25,6 +25,8 @@ import { AzureDevOpsApi } from './AzureDevOpsApi';
|
||||
import { Logger } from 'winston';
|
||||
import limiterFactory from 'p-limit';
|
||||
|
||||
export const DEFAULT_TEAMS_LIMIT = 100;
|
||||
|
||||
export class PullRequestsDashboardProvider {
|
||||
private teams = new Map<string, Team>();
|
||||
|
||||
@@ -43,10 +45,10 @@ export class PullRequestsDashboardProvider {
|
||||
return provider;
|
||||
}
|
||||
|
||||
public async readTeams(): Promise<void> {
|
||||
public async readTeams(limit?: number): Promise<void> {
|
||||
this.logger.info('Reading teams.');
|
||||
|
||||
let teams = await this.azureDevOpsApi.getAllTeams();
|
||||
let teams = await this.azureDevOpsApi.getAllTeams({ limit });
|
||||
|
||||
// This is used to filter out the default Azure Devops project teams.
|
||||
teams = teams.filter(team =>
|
||||
@@ -110,7 +112,7 @@ export class PullRequestsDashboardProvider {
|
||||
const dashboardPullRequests =
|
||||
await this.azureDevOpsApi.getDashboardPullRequests(projectName, options);
|
||||
|
||||
await this.getAllTeams(); // Make sure team members are loaded
|
||||
await this.getAllTeams({ limit: options.teamsLimit }); // Make sure team members are loaded
|
||||
|
||||
return dashboardPullRequests.map(pr => {
|
||||
if (pr.createdBy?.id) {
|
||||
@@ -126,7 +128,7 @@ export class PullRequestsDashboardProvider {
|
||||
}
|
||||
|
||||
public async getUserTeamIds(email: string): Promise<string[]> {
|
||||
await this.getAllTeams(); // Make sure team members are loaded
|
||||
await this.getAllTeams({}); // Make sure team members are loaded
|
||||
return (
|
||||
Array.from(this.teamMembers.values()).find(
|
||||
teamMember => teamMember.uniqueName === email,
|
||||
@@ -134,9 +136,10 @@ export class PullRequestsDashboardProvider {
|
||||
);
|
||||
}
|
||||
|
||||
public async getAllTeams(): Promise<Team[]> {
|
||||
public async getAllTeams(options: { limit?: number }): Promise<Team[]> {
|
||||
if (!this.teams.size) {
|
||||
await this.readTeams();
|
||||
const maxTeams = options?.limit ?? DEFAULT_TEAMS_LIMIT;
|
||||
await this.readTeams(maxTeams);
|
||||
}
|
||||
|
||||
return Array.from(this.teams.values());
|
||||
|
||||
@@ -348,7 +348,74 @@ describe('createRouter', () => {
|
||||
expect(azureDevOpsApi.getPullRequests).toHaveBeenCalledWith(
|
||||
'myProject',
|
||||
'myRepo',
|
||||
{ status: 1, top: 50 },
|
||||
{ status: 1, top: 50, teamsLimit: 100 },
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual(pullRequests);
|
||||
});
|
||||
it('fetches a list of pull requests when using teamsLimit', async () => {
|
||||
const firstPullRequest: PullRequest = {
|
||||
pullRequestId: 7181,
|
||||
repoName: 'super-feature-repo',
|
||||
title: 'My Awesome New Feature',
|
||||
createdBy: 'Jane Doe',
|
||||
creationDate: '2020-09-12T06:10:23.932Z',
|
||||
sourceRefName: 'refs/heads/topic/super-awesome-feature',
|
||||
targetRefName: 'refs/heads/main',
|
||||
status: PullRequestStatus.Active,
|
||||
isDraft: false,
|
||||
link: 'https://host.com/myOrg/_git/super-feature-repo/pullrequest/7181',
|
||||
};
|
||||
|
||||
const secondPullRequest: PullRequest = {
|
||||
pullRequestId: 7182,
|
||||
repoName: 'super-feature-repo',
|
||||
title: 'Refactoring My Awesome New Feature',
|
||||
createdBy: 'Jane Doe',
|
||||
creationDate: '2020-09-12T06:10:23.932Z',
|
||||
sourceRefName: 'refs/heads/topic/refactor-super-awesome-feature',
|
||||
targetRefName: 'refs/heads/main',
|
||||
status: PullRequestStatus.Active,
|
||||
isDraft: false,
|
||||
link: 'https://host.com/myOrg/_git/super-feature-repo/pullrequest/7182',
|
||||
};
|
||||
|
||||
const thirdPullRequest: PullRequest = {
|
||||
pullRequestId: 7183,
|
||||
repoName: 'super-feature-repo',
|
||||
title: 'Bug Fix for My Awesome New Feature',
|
||||
createdBy: 'Jane Doe',
|
||||
creationDate: '2020-09-12T06:10:23.932Z',
|
||||
sourceRefName: 'refs/heads/topic/fix-super-awesome-feature',
|
||||
targetRefName: 'refs/heads/main',
|
||||
status: PullRequestStatus.Active,
|
||||
isDraft: false,
|
||||
link: 'https://host.com/myOrg/_git/super-feature-repo/pullrequest/7183',
|
||||
};
|
||||
|
||||
const pullRequests: PullRequest[] = [
|
||||
firstPullRequest,
|
||||
secondPullRequest,
|
||||
thirdPullRequest,
|
||||
];
|
||||
|
||||
mockedAuthorize.mockImplementationOnce(async () => [
|
||||
{ result: AuthorizeResult.ALLOW },
|
||||
]);
|
||||
|
||||
azureDevOpsApi.getPullRequests.mockResolvedValueOnce(pullRequests);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/pull-requests/myProject/myRepo')
|
||||
.query({ entityRef: 'component:default/mycomponent' })
|
||||
.query({ top: '50', status: 1, teamsLimit: 50 });
|
||||
|
||||
expect(azureDevOpsApi.getPullRequests).toHaveBeenCalledWith(
|
||||
'myProject',
|
||||
'myRepo',
|
||||
{ status: 1, top: 50, teamsLimit: 50 },
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
|
||||
@@ -23,7 +23,10 @@ import {
|
||||
import { AzureDevOpsApi } from '../api';
|
||||
import { Config } from '@backstage/config';
|
||||
import { Logger } from 'winston';
|
||||
import { PullRequestsDashboardProvider } from '../api/PullRequestsDashboardProvider';
|
||||
import {
|
||||
PullRequestsDashboardProvider,
|
||||
DEFAULT_TEAMS_LIMIT,
|
||||
} from '../api/PullRequestsDashboardProvider';
|
||||
import Router from 'express-promise-router';
|
||||
import { errorHandler, UrlReader } from '@backstage/backend-common';
|
||||
import express from 'express';
|
||||
@@ -175,6 +178,9 @@ export async function createRouter(
|
||||
const { projectName, repoName } = req.params;
|
||||
|
||||
const top = req.query.top ? Number(req.query.top) : DEFAULT_TOP;
|
||||
const teamsLimit = req.query.teamsLimit
|
||||
? Number(req.query.teamsLimit)
|
||||
: DEFAULT_TEAMS_LIMIT;
|
||||
const host = req.query.host?.toString();
|
||||
const org = req.query.org?.toString();
|
||||
const status = req.query.status
|
||||
@@ -184,6 +190,7 @@ export async function createRouter(
|
||||
const pullRequestOptions: PullRequestOptions = {
|
||||
top: top,
|
||||
status: status,
|
||||
teamsLimit: teamsLimit,
|
||||
};
|
||||
|
||||
const entityRef = req.query.entityRef;
|
||||
@@ -227,6 +234,9 @@ export async function createRouter(
|
||||
const { projectName } = req.params;
|
||||
|
||||
const top = req.query.top ? Number(req.query.top) : DEFAULT_TOP;
|
||||
const teamsLimit = req.query.teamsLimit
|
||||
? Number(req.query.teamsLimit)
|
||||
: DEFAULT_TEAMS_LIMIT;
|
||||
|
||||
const status = req.query.status
|
||||
? Number(req.query.status)
|
||||
@@ -235,6 +245,7 @@ export async function createRouter(
|
||||
const pullRequestOptions: PullRequestOptions = {
|
||||
top: top,
|
||||
status: status,
|
||||
teamsLimit: teamsLimit,
|
||||
};
|
||||
|
||||
const token = getBearerTokenFromAuthorizationHeader(
|
||||
@@ -266,8 +277,9 @@ export async function createRouter(
|
||||
res.status(200).json(pullRequests);
|
||||
});
|
||||
|
||||
router.get('/all-teams', async (_req, res) => {
|
||||
const allTeams = await pullRequestsDashboardProvider.getAllTeams();
|
||||
router.get('/all-teams', async (req, res) => {
|
||||
const limit = req.query.limit ? Number(req.query.limit) : undefined;
|
||||
const allTeams = await pullRequestsDashboardProvider.getAllTeams({ limit });
|
||||
res.status(200).json(allTeams);
|
||||
});
|
||||
|
||||
|
||||
@@ -215,6 +215,7 @@ export type PullRequest = {
|
||||
export type PullRequestOptions = {
|
||||
top: number;
|
||||
status: PullRequestStatus;
|
||||
teamsLimit?: number;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
|
||||
@@ -142,6 +142,7 @@ export type PullRequest = {
|
||||
export type PullRequestOptions = {
|
||||
top: number;
|
||||
status: PullRequestStatus;
|
||||
teamsLimit?: number;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
|
||||
@@ -65,7 +65,7 @@ export type AssignedToUserFilter = BaseFilter &
|
||||
// @public (undocumented)
|
||||
export interface AzureDevOpsApi {
|
||||
// (undocumented)
|
||||
getAllTeams(): Promise<Team[]>;
|
||||
getAllTeams(limit?: number): Promise<Team[]>;
|
||||
// (undocumented)
|
||||
getBuildRuns(
|
||||
projectName: string,
|
||||
@@ -81,6 +81,7 @@ export interface AzureDevOpsApi {
|
||||
// (undocumented)
|
||||
getDashboardPullRequests(
|
||||
projectName: string,
|
||||
teamsLimit?: number,
|
||||
): Promise<DashboardPullRequest[]>;
|
||||
// (undocumented)
|
||||
getGitTags(
|
||||
@@ -126,7 +127,7 @@ export const azureDevOpsApiRef: ApiRef<AzureDevOpsApi>;
|
||||
export class AzureDevOpsClient implements AzureDevOpsApi {
|
||||
constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi });
|
||||
// (undocumented)
|
||||
getAllTeams(): Promise<Team[]>;
|
||||
getAllTeams(limit?: number): Promise<Team[]>;
|
||||
// (undocumented)
|
||||
getBuildRuns(
|
||||
projectName: string,
|
||||
@@ -142,6 +143,7 @@ export class AzureDevOpsClient implements AzureDevOpsApi {
|
||||
// (undocumented)
|
||||
getDashboardPullRequests(
|
||||
projectName: string,
|
||||
teamsLimit?: number,
|
||||
): Promise<DashboardPullRequest[]>;
|
||||
// (undocumented)
|
||||
getGitTags(
|
||||
@@ -193,6 +195,7 @@ export const AzurePullRequestsPage: (props: {
|
||||
projectName?: string | undefined;
|
||||
pollingInterval?: number | undefined;
|
||||
defaultColumnConfigs?: PullRequestColumnConfig[] | undefined;
|
||||
teamsLimit?: number | undefined;
|
||||
}) => JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
|
||||
@@ -64,9 +64,10 @@ export interface AzureDevOpsApi {
|
||||
|
||||
getDashboardPullRequests(
|
||||
projectName: string,
|
||||
teamsLimit?: number,
|
||||
): Promise<DashboardPullRequest[]>;
|
||||
|
||||
getAllTeams(): Promise<Team[]>;
|
||||
getAllTeams(limit?: number): Promise<Team[]>;
|
||||
|
||||
getUserTeamIds(userId: string): Promise<string[]>;
|
||||
|
||||
|
||||
@@ -107,6 +107,9 @@ export class AzureDevOpsClient implements AzureDevOpsApi {
|
||||
if (options?.status) {
|
||||
queryString.append('status', options.status.toString());
|
||||
}
|
||||
if (options?.teamsLimit) {
|
||||
queryString.append('teamsLimit', options.teamsLimit.toString());
|
||||
}
|
||||
if (host) {
|
||||
queryString.append('host', host);
|
||||
}
|
||||
@@ -124,14 +127,27 @@ export class AzureDevOpsClient implements AzureDevOpsApi {
|
||||
|
||||
public getDashboardPullRequests(
|
||||
projectName: string,
|
||||
teamsLimit?: number,
|
||||
): Promise<DashboardPullRequest[]> {
|
||||
return this.get<DashboardPullRequest[]>(
|
||||
`dashboard-pull-requests/${projectName}?top=100`,
|
||||
);
|
||||
const queryString = new URLSearchParams();
|
||||
queryString.append('top', '100');
|
||||
if (teamsLimit) {
|
||||
queryString.append('teamsLimit', teamsLimit.toString());
|
||||
}
|
||||
const urlSegment = `dashboard-pull-requests/${projectName}?${queryString}`;
|
||||
return this.get<DashboardPullRequest[]>(urlSegment);
|
||||
}
|
||||
|
||||
public getAllTeams(): Promise<Team[]> {
|
||||
return this.get<Team[]>('all-teams');
|
||||
public getAllTeams(limit?: number): Promise<Team[]> {
|
||||
const queryString = new URLSearchParams();
|
||||
if (limit) {
|
||||
queryString.append('limit', limit.toString());
|
||||
}
|
||||
let urlSegment = 'all-teams';
|
||||
if (queryString.toString()) {
|
||||
urlSegment += `?${queryString}`;
|
||||
}
|
||||
return this.get<Team[]>(urlSegment);
|
||||
}
|
||||
|
||||
public getUserTeamIds(userId: string): Promise<string[]> {
|
||||
|
||||
@@ -70,14 +70,17 @@ type PullRequestsPageProps = {
|
||||
projectName?: string;
|
||||
pollingInterval?: number;
|
||||
defaultColumnConfigs?: PullRequestColumnConfig[];
|
||||
teamsLimit?: number;
|
||||
};
|
||||
|
||||
export const PullRequestsPage = (props: PullRequestsPageProps) => {
|
||||
const { projectName, pollingInterval, defaultColumnConfigs } = props;
|
||||
const { projectName, pollingInterval, defaultColumnConfigs, teamsLimit } =
|
||||
props;
|
||||
|
||||
const { pullRequests, loading, error } = useDashboardPullRequests(
|
||||
projectName,
|
||||
pollingInterval,
|
||||
teamsLimit,
|
||||
);
|
||||
|
||||
const [columnConfigs] = useState(
|
||||
|
||||
@@ -27,6 +27,7 @@ const POLLING_INTERVAL = 10000;
|
||||
export function useDashboardPullRequests(
|
||||
project?: string,
|
||||
pollingInterval: number = POLLING_INTERVAL,
|
||||
teamsLimit?: number,
|
||||
): {
|
||||
pullRequests?: DashboardPullRequest[];
|
||||
loading: boolean;
|
||||
@@ -43,7 +44,7 @@ export function useDashboardPullRequests(
|
||||
}
|
||||
|
||||
try {
|
||||
return await api.getDashboardPullRequests(project);
|
||||
return await api.getDashboardPullRequests(project, teamsLimit);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
errorApi.post(error);
|
||||
@@ -51,7 +52,7 @@ export function useDashboardPullRequests(
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}, [project, api, errorApi]);
|
||||
}, [project, api, teamsLimit, errorApi]);
|
||||
|
||||
const {
|
||||
value: pullRequests,
|
||||
|
||||
@@ -31,16 +31,19 @@ export function usePullRequests(
|
||||
entity: Entity,
|
||||
defaultLimit?: number,
|
||||
requestedStatus?: PullRequestStatus,
|
||||
defaultTeamsLimit?: number,
|
||||
): {
|
||||
items?: PullRequest[];
|
||||
loading: boolean;
|
||||
error?: Error;
|
||||
} {
|
||||
const top = defaultLimit ?? AZURE_DEVOPS_DEFAULT_TOP;
|
||||
const teamsLimit = defaultTeamsLimit ?? undefined;
|
||||
const status = requestedStatus ?? PullRequestStatus.Active;
|
||||
const options: PullRequestOptions = {
|
||||
top,
|
||||
status,
|
||||
teamsLimit,
|
||||
};
|
||||
|
||||
const api = useApi(azureDevOpsApiRef);
|
||||
|
||||
Reference in New Issue
Block a user