Use AzureDevOpsCredentialsProvider

Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com>
This commit is contained in:
Andre Wanlin
2023-08-25 15:17:03 -05:00
committed by Andre Wanlin
parent 11a24e8a3a
commit 844969cd97
7 changed files with 69 additions and 30 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-azure-devops-backend': patch
---
Added support for using `AzureDevOpsCredentialsProvider` and deprecated `azureDevOps.token` configuration value
+1 -2
View File
@@ -20,11 +20,10 @@ 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 { UrlReader } from '@backstage/backend-common';
import { WebApi } from 'azure-devops-node-api';
// @public (undocumented)
export class AzureDevOpsApi {
constructor(logger: Logger, webApi: WebApi, urlReader: UrlReader);
constructor(logger: Logger, urlReader: UrlReader, config: Config);
// (undocumented)
getAllTeams(): Promise<Team[]>;
// (undocumented)
+2
View File
@@ -24,6 +24,8 @@ export interface Config {
/**
* Token used to authenticate requests.
* @visibility secret
* @deprecated Use `integrations.azure` instead.
* @see https://backstage.io/docs/integrations/azure/locations
*/
token: string;
/**
@@ -31,6 +31,7 @@
"@backstage/backend-common": "workspace:^",
"@backstage/backend-plugin-api": "workspace:^",
"@backstage/config": "workspace:^",
"@backstage/integration": "workspace:^",
"@backstage/plugin-azure-devops-common": "workspace:^",
"@types/express": "^4.17.6",
"azure-devops-node-api": "^11.0.1",
@@ -49,23 +49,51 @@ import {
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 { WebApi } from 'azure-devops-node-api';
import { WebApi, getPersonalAccessTokenHandler } from 'azure-devops-node-api';
import {
TeamProjectReference,
WebApiTeam,
} from 'azure-devops-node-api/interfaces/CoreInterfaces';
import { UrlReader } from '@backstage/backend-common';
import { Config } from '@backstage/config';
import {
DefaultAzureDevOpsCredentialsProvider,
ScmIntegrations,
} from '@backstage/integration';
/** @public */
export class AzureDevOpsApi {
public constructor(
private readonly logger: Logger,
private readonly webApi: WebApi,
private readonly urlReader: UrlReader,
private readonly config: Config,
) {}
private async getWebApi(host?: string, org?: string): Promise<WebApi> {
const validHost = host ?? this.config.getString('azureDevOps.host');
const validOrg = org ?? this.config.getString('azureDevOps.organization');
const scmIntegrations = ScmIntegrations.fromConfig(this.config);
const credentialsProvider =
DefaultAzureDevOpsCredentialsProvider.fromIntegrations(scmIntegrations);
const credentials = await credentialsProvider.getCredentials({
url: `https://${validHost}/${validOrg}`,
});
let validToken: string;
if (credentials && credentials.token) {
validToken = credentials.token;
} else {
validToken = this.config.getString('azureDevOps.token');
}
const authHandler = getPersonalAccessTokenHandler(validToken);
const webApi = new WebApi(`https://${validHost}/${validOrg}`, authHandler);
return webApi;
}
public async getProjects(): Promise<Project[]> {
const client = await this.webApi.getCoreApi();
const webApi = await this.getWebApi();
const client = await webApi.getCoreApi();
const projectList: TeamProjectReference[] = await client.getProjects();
const projects: Project[] = projectList.map(project => ({
@@ -87,7 +115,8 @@ export class AzureDevOpsApi {
`Calling Azure DevOps REST API, getting Repository ${repoName} for Project ${projectName}`,
);
const client = await this.webApi.getGitApi();
const webApi = await this.getWebApi();
const client = await webApi.getGitApi();
return client.getRepository(repoName, projectName);
}
@@ -100,7 +129,8 @@ export class AzureDevOpsApi {
`Calling Azure DevOps REST API, getting up to ${top} Builds for Repository Id ${repoId} for Project ${projectName}`,
);
const client = await this.webApi.getBuildApi();
const webApi = await this.getWebApi();
const client = await webApi.getBuildApi();
return client.getBuilds(
projectName,
undefined,
@@ -158,7 +188,8 @@ export class AzureDevOpsApi {
);
const gitRepository = await this.getGitRepository(projectName, repoName);
const client = await this.webApi.getGitApi();
const webApi = await this.getWebApi();
const client = await webApi.getGitApi();
const tagRefs: GitRef[] = await client.getRefs(
gitRepository.id as string,
projectName,
@@ -169,10 +200,10 @@ export class AzureDevOpsApi {
false,
true,
);
const linkBaseUrl = `${this.webApi.serverUrl}/${encodeURIComponent(
const linkBaseUrl = `${webApi.serverUrl}/${encodeURIComponent(
projectName,
)}/_git/${encodeURIComponent(repoName)}?version=GT`;
const commitBaseUrl = `${this.webApi.serverUrl}/${encodeURIComponent(
const commitBaseUrl = `${webApi.serverUrl}/${encodeURIComponent(
projectName,
)}/_git/${encodeURIComponent(repoName)}/commit`;
const gitTags: GitTag[] = tagRefs.map(tagRef => {
@@ -192,7 +223,8 @@ export class AzureDevOpsApi {
);
const gitRepository = await this.getGitRepository(projectName, repoName);
const client = await this.webApi.getGitApi();
const webApi = await this.getWebApi();
const client = await webApi.getGitApi();
const searchCriteria: GitPullRequestSearchCriteria = {
status: options.status,
};
@@ -204,7 +236,7 @@ export class AzureDevOpsApi {
undefined,
options.top,
);
const linkBaseUrl = `${this.webApi.serverUrl}/${encodeURIComponent(
const linkBaseUrl = `${webApi.serverUrl}/${encodeURIComponent(
projectName,
)}/_git/${encodeURIComponent(repoName)}/pullrequest`;
const pullRequests: PullRequest[] = gitPullRequests.map(gitPullRequest => {
@@ -222,7 +254,8 @@ export class AzureDevOpsApi {
`Getting dashboard pull requests for project '${projectName}'.`,
);
const client = await this.webApi.getGitApi();
const webApi = await this.getWebApi();
const client = await webApi.getGitApi();
const searchCriteria: GitPullRequestSearchCriteria = {
status: options.status,
@@ -254,7 +287,7 @@ export class AzureDevOpsApi {
return convertDashboardPullRequest(
gitPullRequest,
this.webApi.serverUrl,
webApi.serverUrl,
policies,
);
}),
@@ -270,7 +303,8 @@ export class AzureDevOpsApi {
`Getting pull request policies for pull request id '${pullRequestId}'.`,
);
const client = await this.webApi.getPolicyApi();
const webApi = await this.getWebApi();
const client = await webApi.getPolicyApi();
const artifactId = getArtifactId(projectId, pullRequestId);
@@ -285,7 +319,8 @@ export class AzureDevOpsApi {
public async getAllTeams(): Promise<Team[]> {
this.logger?.debug('Getting all teams.');
const client = await this.webApi.getCoreApi();
const webApi = await this.getWebApi();
const client = await webApi.getCoreApi();
const webApiTeams: WebApiTeam[] = await client.getAllTeams();
const teams: Team[] = webApiTeams.map(team => ({
@@ -307,7 +342,8 @@ export class AzureDevOpsApi {
const { projectId, teamId } = options;
this.logger?.debug(`Getting team member ids for team '${teamId}'.`);
const client = await this.webApi.getCoreApi();
const webApi = await this.getWebApi();
const client = await webApi.getCoreApi();
const teamMembers: AdoTeamMember[] =
await client.getTeamMembersWithExtendedProperties(projectId, teamId);
@@ -327,7 +363,8 @@ export class AzureDevOpsApi {
`Calling Azure DevOps REST API, getting Build Definitions for ${definitionName} in Project ${projectName}`,
);
const client = await this.webApi.getBuildApi();
const webApi = await this.getWebApi();
const client = await webApi.getBuildApi();
return client.getDefinitions(projectName, definitionName);
}
@@ -341,7 +378,8 @@ export class AzureDevOpsApi {
`Calling Azure DevOps REST API, getting up to ${top} Builds for Repository Id ${repoId} for Project ${projectName}`,
);
const client = await this.webApi.getBuildApi();
const webApi = await this.getWebApi();
const client = await webApi.getBuildApi();
return client.getBuilds(
projectName,
definitions,
@@ -19,7 +19,6 @@ import {
PullRequestOptions,
PullRequestStatus,
} from '@backstage/plugin-azure-devops-common';
import { WebApi, getPersonalAccessTokenHandler } from 'azure-devops-node-api';
import { AzureDevOpsApi } from '../api';
import { Config } from '@backstage/config';
@@ -43,18 +42,10 @@ export interface RouterOptions {
export async function createRouter(
options: RouterOptions,
): Promise<express.Router> {
const { logger, reader } = options;
const config = options.config.getConfig('azureDevOps');
const token = config.getString('token');
const host = config.getString('host');
const organization = config.getString('organization');
const authHandler = getPersonalAccessTokenHandler(token);
const webApi = new WebApi(`https://${host}/${organization}`, authHandler);
const { logger, reader, config } = options;
const azureDevOpsApi =
options.azureDevOpsApi || new AzureDevOpsApi(logger, webApi, reader);
options.azureDevOpsApi || new AzureDevOpsApi(logger, reader, config);
const pullRequestsDashboardProvider =
await PullRequestsDashboardProvider.create(logger, azureDevOpsApi);
@@ -195,6 +186,8 @@ export async function createRouter(
});
router.get('/readme/:projectName/:repoName', async (req, res) => {
const host = config.getString('azureDevOps.host');
const organization = config.getString('azureDevOps.organization');
const { projectName, repoName } = req.params;
const readme = await azureDevOpsApi.getReadme(
host,
+1
View File
@@ -5077,6 +5077,7 @@ __metadata:
"@backstage/backend-plugin-api": "workspace:^"
"@backstage/cli": "workspace:^"
"@backstage/config": "workspace:^"
"@backstage/integration": "workspace:^"
"@backstage/plugin-azure-devops-common": "workspace:^"
"@types/express": ^4.17.6
"@types/supertest": ^2.0.8