backend-common: added compatibility wrapper for legacy plugins

Co-authored-by: Johan Haals <johan.haals@gmail.com>
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2023-01-11 12:05:20 +01:00
parent 843a0a158c
commit 6e2248abbe
3 changed files with 178 additions and 0 deletions
+53
View File
@@ -9,6 +9,7 @@
import aws from 'aws-sdk';
import { AwsS3Integration } from '@backstage/integration';
import { AzureIntegration } from '@backstage/integration';
import { BackendFeature } from '@backstage/backend-plugin-api';
import { BitbucketCloudIntegration } from '@backstage/integration';
import { BitbucketIntegration } from '@backstage/integration';
import { BitbucketServerIntegration } from '@backstage/integration';
@@ -16,6 +17,7 @@ import { CacheClient } from '@backstage/backend-plugin-api';
import { CacheClientOptions } from '@backstage/backend-plugin-api';
import { CacheClientSetOptions } from '@backstage/backend-plugin-api';
import { Config } from '@backstage/config';
import { ConfigService } from '@backstage/backend-plugin-api';
import cors from 'cors';
import Docker from 'dockerode';
import { Duration } from 'luxon';
@@ -26,6 +28,7 @@ import { GiteaIntegration } from '@backstage/integration';
import { GithubCredentialsProvider } from '@backstage/integration';
import { GithubIntegration } from '@backstage/integration';
import { GitLabIntegration } from '@backstage/integration';
import { IdentityService } from '@backstage/backend-plugin-api';
import { isChildPath } from '@backstage/cli-common';
import { Knex } from 'knex';
import { KubeConfig } from '@kubernetes/client-node';
@@ -33,6 +36,7 @@ import { LoadConfigOptionsRemote } from '@backstage/config-loader';
import { Logger } from 'winston';
import { LoggerService } from '@backstage/backend-plugin-api';
import { MergeResult } from 'isomorphic-git';
import { PermissionsService } from '@backstage/backend-plugin-api';
import { CacheService as PluginCacheManager } from '@backstage/backend-plugin-api';
import { DatabaseService as PluginDatabaseManager } from '@backstage/backend-plugin-api';
import { DiscoveryService as PluginEndpointDiscovery } from '@backstage/backend-plugin-api';
@@ -47,10 +51,12 @@ import { ReadUrlOptions } from '@backstage/backend-plugin-api';
import { ReadUrlResponse } from '@backstage/backend-plugin-api';
import { RequestHandler } from 'express';
import { Router } from 'express';
import { SchedulerService } from '@backstage/backend-plugin-api';
import { SearchOptions } from '@backstage/backend-plugin-api';
import { SearchResponse } from '@backstage/backend-plugin-api';
import { SearchResponseFile } from '@backstage/backend-plugin-api';
import { Server } from 'http';
import { ServiceRef } from '@backstage/backend-plugin-api';
import { TokenManagerService as TokenManager } from '@backstage/backend-plugin-api';
import { TransportStreamOptions } from 'winston-transport';
import { UrlReaderService as UrlReader } from '@backstage/backend-plugin-api';
@@ -228,6 +234,32 @@ export function createDatabaseClient(
overrides?: Partial<Knex.Config>,
): Knex<any, any[]>;
// @public
export const createPluginCompat: (
name: string,
createRouterImport: Promise<{
default: LegacyCreateRouter<
TransformedEnv<
{
cache: PluginCacheManager;
config: ConfigService;
database: PluginDatabaseManager;
discovery: PluginEndpointDiscovery;
logger: LoggerService;
permissions: PermissionsService;
scheduler: SchedulerService;
tokenManager: TokenManager;
reader: UrlReader;
identity: IdentityService;
},
{
logger: (log: LoggerService) => Logger;
}
>
>;
}>,
) => BackendFeature;
// @public
export function createRootLogger(
options?: winston.LoggerOptions,
@@ -500,6 +532,9 @@ export type KubernetesContainerRunnerOptions = {
timeoutMs?: number;
};
// @public (undocumented)
export type LegacyCreateRouter<TEnv> = (deps: TEnv) => Promise<RequestHandler>;
// @public
export function loadBackendConfig(options: {
logger: LoggerService;
@@ -513,6 +548,24 @@ export function loggerToWinstonLogger(
opts?: TransportStreamOptions,
): Logger;
// @public
export function makePluginCompat<
TEnv extends Record<string, unknown>,
TEnvTransforms extends {
[key in keyof TEnv]?: (dep: TEnv[key]) => unknown;
},
>(
envMapping: {
[key in keyof TEnv]: ServiceRef<TEnv[key]>;
},
envTransforms: TEnvTransforms,
): (
name: string,
createRouterImport: Promise<{
default: LegacyCreateRouter<TransformedEnv<TEnv, TEnvTransforms>>;
}>,
) => BackendFeature;
// @public
export function notFoundHandler(): RequestHandler;
+123
View File
@@ -0,0 +1,123 @@
/*
* Copyright 2023 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 {
coreServices,
createBackendPlugin,
ServiceRef,
} from '@backstage/backend-plugin-api';
import { RequestHandler } from 'express';
import { loggerToWinstonLogger } from './logging';
/**
* @public
*/
export type LegacyCreateRouter<TEnv> = (deps: TEnv) => Promise<RequestHandler>;
/** @ignore */
type TransformedEnv<
TEnv extends Record<string, unknown>,
TEnvTransforms extends { [key in keyof TEnv]?: (dep: TEnv[key]) => unknown },
> = {
[key in keyof TEnv]: TEnvTransforms[key] extends (dep: TEnv[key]) => infer R
? R
: TEnv[key];
};
/**
* Creates a new custom plugin compatibility wrapper.
*
* @public
* @remarks
*
* Usually you can use {@link createPluginCompat} directly instead, but you might
* need to use this if you have customized the plugin environment in your backend.
*/
export function makePluginCompat<
TEnv extends Record<string, unknown>,
TEnvTransforms extends { [key in keyof TEnv]?: (dep: TEnv[key]) => unknown },
>(
envMapping: { [key in keyof TEnv]: ServiceRef<TEnv[key]> },
envTransforms: TEnvTransforms,
) {
return (
name: string,
createRouterImport: Promise<{
default: LegacyCreateRouter<TransformedEnv<TEnv, TEnvTransforms>>;
}>,
) => {
const compatPlugin = createBackendPlugin({
id: name,
register(env) {
env.registerInit({
deps: { ...envMapping, _router: coreServices.httpRouter },
async init({ _router, ...envDeps }) {
const { default: createRouter } = await createRouterImport;
const pluginEnv = Object.fromEntries(
Object.entries(envDeps).map(([key, dep]) => {
const transform = envTransforms[key];
if (transform) {
return [key, transform(dep)];
}
return [key, dep];
}),
);
const router = await createRouter(
pluginEnv as TransformedEnv<TEnv, TEnvTransforms>,
);
_router.use(router);
},
});
},
});
return compatPlugin();
};
}
/**
* Helper function to create a plugin from a legacy createRouter function and
* register it with the http router based on the plugin id.
*
* @public
* @remarks
*
* This is intended to be used by plugin authors to ease the transition to the
* new backend system.
*
* @example
*
*```ts
*backend.add(createPluginCompat('kafka', import('./plugins/kafka')));
*```
*/
export const createPluginCompat = makePluginCompat(
{
cache: coreServices.cache,
config: coreServices.config,
database: coreServices.database,
discovery: coreServices.discovery,
logger: coreServices.logger,
permissions: coreServices.permissions,
scheduler: coreServices.scheduler,
tokenManager: coreServices.tokenManager,
reader: coreServices.urlReader,
identity: coreServices.identity,
},
{
logger: log => loggerToWinstonLogger(log),
},
);
+2
View File
@@ -20,6 +20,8 @@
* @packageDocumentation
*/
export { makePluginCompat, createPluginCompat } from './compat';
export type { LegacyCreateRouter } from './compat';
export * from './cache';
export { loadBackendConfig } from './config';
export * from './context';