diff --git a/dev/index.ts b/dev/index.ts deleted file mode 100644 index 6918883..0000000 --- a/dev/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { createBackend } from '@backstage/backend-defaults'; - -const backend = createBackend(); - -// backend.add(import('@backstage/plugin-auth-backend')); -// backend.add(import('@backstage/plugin-auth-backend-module-guest-provider')); -backend.add(import('../src')); - -backend.start(); \ No newline at end of file diff --git a/package.json b/package.json index 9b6087d..e5cefda 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "types": "dist/index.d.ts" }, "backstage": { - "role": "backend-plugin" + "role": "backend-plugin-module" }, "scripts": { "start": "yarn tsc && backstage-cli package start", @@ -22,8 +22,10 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { + "@backstage/backend-common": "^0.21.6", "@backstage/backend-defaults": "^0.2.16", "@backstage/backend-plugin-api": "^0.6.16", + "@backstage/config": "^1.2.0", "@backstage/plugin-scaffolder-node": "^0.4.2", "@rjsf/core": "^5.14.3", "node-fetch": "^2.6.7", diff --git a/src/actions/custom.ts b/src/actions/custom.ts index 01159c6..3c7829b 100644 --- a/src/actions/custom.ts +++ b/src/actions/custom.ts @@ -2,8 +2,19 @@ import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; import { z } from 'zod'; import * as api from '../apis/pagerduty'; import { CreateServiceResponse } from '../types'; +import { loadAuthConfig } from '../auth/auth'; +import { LoggerService, RootConfigService } from '@backstage/backend-plugin-api'; +import { Config } from "@backstage/config"; +import { loadBackendConfig } from "@backstage/backend-common"; -export const createPagerDutyServiceAction = () => { +export type CreatePagerDutyServiceActionProps = { + config: RootConfigService; + logger: LoggerService; +}; + +export const createPagerDutyServiceAction = (props : CreatePagerDutyServiceActionProps) => { + + let loggerService: LoggerService; return createTemplateAction<{ name: string; @@ -27,15 +38,30 @@ export const createPagerDutyServiceAction = () => { }, async handler(ctx) { - try { + try { + loggerService = props?.logger ? props.logger : ctx.logger; + const configService = props?.config ?? props.config; + + const legacyConfig: Config = await loadBackendConfig({ + logger: loggerService, + argv: [], + }); + + // Load the auth configuration + await loadAuthConfig({ + config: configService, + legacyConfig: legacyConfig, + logger: loggerService, + }); + // Create service in PagerDuty const service: CreateServiceResponse = await api.createService( ctx.input.name, ctx.input.description, ctx.input.escalationPolicyId, ctx.input.alertGrouping); - ctx.logger.info(`Service '${ctx.input.name}' created successfully!`); - ctx.logger.info(`Alert grouping set to '${service.alertGrouping}'`); + loggerService.info(`Service '${ctx.input.name}' created successfully!`); + loggerService.info(`Alert grouping set to '${service.alertGrouping}'`); ctx.output('serviceUrl', service.url); ctx.output('serviceId', service.id); @@ -43,11 +69,11 @@ export const createPagerDutyServiceAction = () => { // Create Backstage Integration in PagerDuty service const backstageIntegrationId = 'PRO19CT'; // ID for Backstage integration const integrationKey = await api.createServiceIntegration(service.id, backstageIntegrationId); - ctx.logger.info(`Backstage Integration for service '${ctx.input.name}' created successfully!`); + loggerService.info(`Backstage Integration for service '${ctx.input.name}' created successfully!`); ctx.output('integrationKey', integrationKey); } catch (error) { - ctx.logger.error(`${error}`); + loggerService.error(`${error}`); } } diff --git a/src/auth/auth.ts b/src/auth/auth.ts index 3142a1a..5cb7f68 100644 --- a/src/auth/auth.ts +++ b/src/auth/auth.ts @@ -1,65 +1,88 @@ import { LoggerService, RootConfigService } from "@backstage/backend-plugin-api"; import { HttpError } from "@pagerduty/backstage-plugin-common"; +import { Config } from "@backstage/config"; type Auth = { - config: RootConfigService; - logger: LoggerService; authToken: string; authTokenExpiryDate: number; } +export type LoadAuthConfigProps = { + config: RootConfigService; + legacyConfig: Config; + logger: LoggerService; +} + +type JsonValue = boolean | number | string | null | JsonArray | JsonObject; + +interface JsonObject { + [x: string]: JsonValue; +} + +type JsonArray = JsonValue[]; + let authPersistence: Auth; +let isLegacyConfig: boolean; +let _config: RootConfigService; +let _legacyConfig: Config; +let _logger: LoggerService; export async function getAuthToken(): Promise { // check if token already exists and is valid if ( - (authPersistence.authToken !== '' && - authPersistence.authToken.includes('Bearer') && + (authPersistence?.authToken?.includes('Bearer') && authPersistence.authTokenExpiryDate > Date.now()) // case where OAuth token is still valid || - (authPersistence.authToken !== '' && - authPersistence.authToken.includes('Token'))) { // case where API token is used + (authPersistence?.authToken?.includes('Token'))) { // case where API token is used return authPersistence.authToken; } - await loadAuthConfig(authPersistence.config, authPersistence.logger); + await loadAuthConfig({ + config: _config, + legacyConfig: _legacyConfig, + logger: _logger, + }); return authPersistence.authToken; } -export async function loadAuthConfig(config : RootConfigService, logger: LoggerService) { +export async function loadAuthConfig({config, legacyConfig, logger}: LoadAuthConfigProps) { try { + // check if we are using new backend system. Fallback to legacy config if not + isLegacyConfig = !config; + + // set config and logger + _config = config; + _legacyConfig = legacyConfig; + _logger = logger; // initiliaze the authPersistence in-memory object authPersistence = { - config, - logger, authToken: '', authTokenExpiryDate: Date.now() }; - if (!config.getOptionalString('pagerDuty.apiToken')) { + if (!readOptionalString('pagerDuty.apiToken')) { logger.warn('No PagerDuty API token found in config file. Trying OAuth token instead...'); - if (!config.getOptional('pagerDuty.oauth')) { - + if (!readOptionalObject('pagerDuty.oauth')) { logger.error('No PagerDuty OAuth configuration found in config file.'); - } else if (!config.getOptionalString('pagerDuty.oauth.clientId') || !config.getOptionalString('pagerDuty.oauth.clientSecret') || !config.getOptionalString('pagerDuty.oauth.subDomain')) { + } else if (!readOptionalString('pagerDuty.oauth.clientId') || !readOptionalString('pagerDuty.oauth.clientSecret') || !readOptionalString('pagerDuty.oauth.subDomain')) { logger.error("Missing required PagerDuty OAuth parameters in config file. 'clientId', 'clientSecret', and 'subDomain' are required. 'region' is optional."); } else { authPersistence.authToken = await getOAuthToken( - config.getString('pagerDuty.oauth.clientId'), - config.getString('pagerDuty.oauth.clientSecret'), - config.getString('pagerDuty.oauth.subDomain'), - config.getOptionalString('pagerDuty.oauth.region') ?? 'us'); + readString('pagerDuty.oauth.clientId'), + readString('pagerDuty.oauth.clientSecret'), + readString('pagerDuty.oauth.subDomain'), + readOptionalString('pagerDuty.oauth.region') ?? 'us'); logger.info('PagerDuty OAuth configuration loaded successfully.'); } } else { - authPersistence.authToken = `Token token=${config.getString('pagerDuty.apiToken')}`; + authPersistence.authToken = `Token token=${readString('pagerDuty.apiToken')}`; logger.info('PagerDuty API token loaded successfully.'); } @@ -69,6 +92,31 @@ export async function loadAuthConfig(config : RootConfigService, logger: LoggerS } } +function readOptionalString(key: string) : string | undefined { + if (isLegacyConfig) { + return _legacyConfig.getOptionalString(key); + } + + return _config.getOptionalString(key); +} + +function readOptionalObject(key: string): JsonValue | undefined { + if (isLegacyConfig) { + return _legacyConfig.getOptional(key); + } + + return _config.getOptional(key); +} + +function readString(key: string) : string { + if (isLegacyConfig) { + return _legacyConfig.getString(key); + } + + return _config.getString(key); +} + + async function getOAuthToken(clientId: string, clientSecret: string, subDomain: string, region: string): Promise { // check if required parameters are provided if (!clientId || !clientSecret || !subDomain) { diff --git a/src/index.ts b/src/index.ts index 18457fd..6291fd1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,2 +1,2 @@ -export { pagerDutyScaffolderActions as default } from './plugin'; -export * from './actions/custom'; +export { pagerDutyScaffolderActions as default } from './module'; +export * from './actions/custom'; \ No newline at end of file diff --git a/src/plugin.ts b/src/module.ts similarity index 63% rename from src/plugin.ts rename to src/module.ts index 2572391..5e392f1 100644 --- a/src/plugin.ts +++ b/src/module.ts @@ -1,7 +1,8 @@ -import { createBackendModule } from "@backstage/backend-plugin-api"; +import { coreServices, createBackendModule } from "@backstage/backend-plugin-api"; import { scaffolderActionsExtensionPoint } from "@backstage/plugin-scaffolder-node/alpha"; import { createPagerDutyServiceAction } from "./actions/custom"; + /** @public */ export const pagerDutyScaffolderActions = createBackendModule({ pluginId: 'scaffolder', @@ -9,10 +10,16 @@ export const pagerDutyScaffolderActions = createBackendModule({ register(env) { env.registerInit({ deps: { + config: coreServices.rootConfig, + logger: coreServices.logger, scaffolder: scaffolderActionsExtensionPoint, }, - async init({ scaffolder }) { - scaffolder.addActions(createPagerDutyServiceAction()); + async init({ config, logger, scaffolder }) { + + scaffolder.addActions(createPagerDutyServiceAction({ + config, + logger + })); }, }); }, diff --git a/src/types.ts b/src/types.ts index 8bf82c4..f9e0db8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,3 +3,11 @@ export type CreateServiceResponse = { url: string; alertGrouping: string; }; + +import { Logger } from 'winston'; +import { Config } from '@backstage/config'; + +export type PluginEnvironment = { + logger: Logger; + config: Config; +}; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index e5922b3..956e9a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4357,9 +4357,11 @@ __metadata: version: 0.0.0-use.local resolution: "@pagerduty/backstage-plugin-scaffolder-actions@workspace:." dependencies: + "@backstage/backend-common": ^0.21.6 "@backstage/backend-defaults": ^0.2.16 "@backstage/backend-plugin-api": ^0.6.16 "@backstage/cli": ^0.24.0 + "@backstage/config": ^1.2.0 "@backstage/plugin-scaffolder-node": ^0.4.2 "@pagerduty/backstage-plugin-common": ^0.1.3 "@rjsf/core": ^5.14.3