Skip to main content

后端插件扩展点

虽然插件可以使用静态配置来实现轻量级的自定义,但很快就会遇到限制,需要更强大的功能来允许用户扩展插件。 为此,后端系统为插件提供了一种提供扩展点的机制,可用于为插件提供更深层次的自定义。 扩展点由模块使用,模块安装在后端,与插件相邻。 模块将在下一节.

扩展点与服务非常相似,都是在引用对象中封装一个接口。 主要区别在于,扩展点是由插件自己注册和提供的,没有任何与之相关的工厂。 特定插件的扩展点也只能由扩展该插件的模块访问。

插件扩展点应始终从插件节点库包中导出,例如@backstage/plugin-catalog-node这是为了让模块避免直接依赖插件,并使扩展点更容易随着时间的推移而发展。 您可以导出任意多个不同的扩展点,但要注意 API 表面的复杂性。 不过,通常情况下,导出方法较少的多个扩展点要比导出方法较多的几个扩展点更好,因为这样往往更容易维护。

定义扩展点

扩展点是使用createExtensionPoint方法从@backstage/backend-plugin-api您需要提供类型和 ID。

import { createExtensionPoint } from '@backstage/backend-plugin-api';

export interface ScaffolderActionsExtensionPoint {
addAction(action: ScaffolderAction): void;
}

export const scaffolderActionsExtensionPoint =
createExtensionPoint<ScaffolderActionsExtensionPoint>({
id: 'scaffolder.actions',
});

注册扩展点

要让模块使用您的扩展点,插件必须注册该扩展点的实现。registerExtensionPoint方法中的register插件定义的回调。

export const scaffolderPlugin = createBackendPlugin(
{
pluginId: 'scaffolder',
register(env) {
const actions = new Map<string, TemplateAction<any>>();

env.registerExtensionPoint(
scaffolderActionsExtensionPoint,
{
addAction(action) {
if (actions.has(action.id)) {
throw new Error(`Scaffolder actions with ID '${action.id}' has already been installed`);
}
actions.set(action.id, action);
},
},
);

env.registerInit({
deps: { ... },
async init({ ... }) {
// Use the registered actions when setting up the scaffolder ...
const installedActions = Array.from(actions.values());
},
});
},
},
);

请注意,我们创建的闭包将添加到共享的actionsaddAction我们可以安全地访问我们的actionsinit方法,因为所有扩展我们插件的模块都将在我们的插件初始化之前完全初始化。 这意味着在我们的init方法被调用时,所有操作都已添加并可被访问。

模块扩展点

与插件一样,模块也可以提供自己的扩展点。 注册和使用扩展点的 API 与插件相同。 不过,模块通常只应使用扩展点,以便插件模块的用户进行复杂的内部定制。 因此,最好直接从模块包中导出扩展点,而不是为此单独创建一个节点库。 模块导出的扩展点与插件导出的扩展点使用方法相同,即创建自己的独立模块,并声明对要交互的扩展点的依赖关系。

扩展点设计

设计扩展点接口需要仔细考虑。 这是一个公共 API 表面,您的插件需要长期维护。 请记住,安装模块是用户的有意行为,这意味着您可以设计一个仅用于添加的扩展点接口。 例如,不需要scaffolderActionsExtensionPoint以支持删除操作,因为用户只需卸载添加操作的模块即可。

另一种可使用的模式是单例模式(singleton pattern),其中扩展点用于添加或覆盖某些默认行为。 例如,假设脚手架想要公开一种自定义执行模板任务的方法。 允许多个模块分别添加自己的任务运行程序并不合理,因此我们使用一个设置器来确保只安装一个任务运行程序,如果其他模块试图安装其他任务运行程序,则会出错。

interface ScaffolderTaskRunnerExtensionPoint {
setTaskRunner(taskRunner: TaskRunner): void;
}

如果您想对已经有一定使用率的扩展点进行破坏性修改,我们建议您弃用现有扩展点,并创建一个名称不同的新扩展点。 您可能想使用一个全新的名称,但也可以在现有扩展点后加上版本号,例如scaffolderActionsV2ExtensionPoint.