From c8f2b807c0573996548e6b9b7edf60f688437673 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Thu, 16 Nov 2023 11:24:11 -0600 Subject: [PATCH] Changes based on latest feedback Signed-off-by: Andre Wanlin --- .changeset/unlucky-clouds-build.md | 6 ++- plugins/azure-devops-backend/config.d.ts | 9 +++- .../src/api/AzureDevOpsApi.ts | 50 ++++++++++++------- 3 files changed, 45 insertions(+), 20 deletions(-) diff --git a/.changeset/unlucky-clouds-build.md b/.changeset/unlucky-clouds-build.md index 7bd1cb0644..6f7813a8e6 100644 --- a/.changeset/unlucky-clouds-build.md +++ b/.changeset/unlucky-clouds-build.md @@ -1,5 +1,7 @@ --- -'@backstage/plugin-azure-devops-backend': patch +'@backstage/plugin-azure-devops-backend': minor --- -Added support for using `AzureDevOpsCredentialsProvider` and deprecated `azureDevOps.token` configuration value +**BREAKING** New `fromConfig` static method must be used now when created an instance of the `AzureDevOpsApi` + +Added support for using the `AzureDevOpsCredentialsProvider` and deprecated the entire `azureDevOps` configuration section diff --git a/plugins/azure-devops-backend/config.d.ts b/plugins/azure-devops-backend/config.d.ts index 39523a7a05..c973a0595f 100644 --- a/plugins/azure-devops-backend/config.d.ts +++ b/plugins/azure-devops-backend/config.d.ts @@ -15,10 +15,15 @@ */ export interface Config { - /** Configuration options for the azure-devops-backend plugin */ + /** Configuration options for the azure-devops-backend plugin + * @deprecated Use `integrations.azure` instead. + * @see https://backstage.io/docs/integrations/azure/locations + */ azureDevOps: { /** * The hostname of the given Azure instance + * @deprecated Use `integrations.azure` instead. + * @see https://backstage.io/docs/integrations/azure/locations */ host: string; /** @@ -30,6 +35,8 @@ export interface Config { token: string; /** * The organization of the given Azure instance + * @deprecated Use `integrations.azure` instead. + * @see https://backstage.io/docs/integrations/azure/locations */ organization: string; }; diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts index bdfd7b1b82..f02d681f56 100644 --- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts +++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts @@ -51,7 +51,7 @@ import { Logger } from 'winston'; import { PolicyEvaluationRecord } from 'azure-devops-node-api/interfaces/PolicyInterfaces'; import { WebApi, - getBearerHandler, + getHandlerFromToken, getPersonalAccessTokenHandler, } from 'azure-devops-node-api'; import { @@ -61,6 +61,7 @@ import { import { UrlReader } from '@backstage/backend-common'; import { Config } from '@backstage/config'; import { + AzureDevOpsCredentialsProvider, DefaultAzureDevOpsCredentialsProvider, ScmIntegrations, } from '@backstage/integration'; @@ -70,42 +71,57 @@ export class AzureDevOpsApi { private readonly logger: Logger; private readonly urlReader: UrlReader; private readonly config: Config; - private readonly credentialsProvider: DefaultAzureDevOpsCredentialsProvider; + private readonly credentialsProvider: AzureDevOpsCredentialsProvider; - private constructor(logger: Logger, urlReader: UrlReader, config: Config) { + private constructor( + logger: Logger, + urlReader: UrlReader, + config: Config, + credentialsProvider: AzureDevOpsCredentialsProvider, + ) { this.logger = logger; this.urlReader = urlReader; this.config = config; - - const scmIntegrations = ScmIntegrations.fromConfig(this.config); - this.credentialsProvider = - DefaultAzureDevOpsCredentialsProvider.fromIntegrations(scmIntegrations); + this.credentialsProvider = credentialsProvider; } static fromConfig( config: Config, options: { logger: Logger; urlReader: UrlReader }, ) { - return new AzureDevOpsApi(options.logger, options.urlReader, config); + const scmIntegrations = ScmIntegrations.fromConfig(config); + const credentialsProvider = + DefaultAzureDevOpsCredentialsProvider.fromIntegrations(scmIntegrations); + return new AzureDevOpsApi( + options.logger, + options.urlReader, + config, + credentialsProvider, + ); } private async getWebApi(host?: string, org?: string): Promise { + // If no host or org is provided we fall back to the values from the `azureDevOps` config section + // these may have been setup in the `integrations.azure` config section + // which is why use them here and not just falling back on them entirely const validHost = host ?? this.config.getString('azureDevOps.host'); const validOrg = org ?? this.config.getString('azureDevOps.organization'); - const url = `https://${validHost}/${validOrg}`; + const url = `https://${validHost}/${encodeURI(validOrg)}`; const credentials = await this.credentialsProvider.getCredentials({ url, }); - // TODO:(awanlin) use `getHandlerFromToken` once we no longer - // need to support the 'azureDevOps.token' fallback config value - const authHandler = - credentials?.type === 'pat' - ? getPersonalAccessTokenHandler( - credentials!.token ?? this.config.getString('azureDevOps.token'), - ) - : getBearerHandler(credentials!.token); + let authHandler; + if (!credentials) { + // No credentials found for the provided host and org in the `integrations.azure` config section + // use the fall back personal access token from `azureDevOps.token` + const token = this.config.getString('azureDevOps.token'); + authHandler = getPersonalAccessTokenHandler(token); + } else { + authHandler = getHandlerFromToken(credentials.token); + } + const webApi = new WebApi(url, authHandler); return webApi; }