Skip to main content

实用 API

简介

Backstage 插件力求自成一体,尽可能多的功能都在插件本身及其后端应用程序接口(API)中。 不过,插件总是需要在其边界之外与其他插件和应用程序本身进行通信。

Backstage 为插件提供了两种在客户端代码中跨边界通信的主要方法。 第一种是创建插件第二种是实用程序应用程序接口。创建插件应用程序接口(API)侧重于初始化插件和应用程序,而实用程序接口(Utility API)则为插件提供了在其整个生命周期内进行通信的方式。

使用应用程序接口

每个实用程序应用编程接口都与ApiRef实例是一个全局单例对象,没有任何附加状态或功能,其唯一用途是引用实用程序编程接口。ApiRef是使用创建 ApiRef@backstage/core-plugin-api中还有许多预定义的实用程序应用程序接口。@backstage/core-plugin-api它们都以模式名称导出*ApiRef例如errorApiRef.

要访问 React 组件中的实用程序应用程序接口,请使用使用应用程序@backstage/core-plugin-api有 Apis如果您喜欢类组件,请使用 HOC。ErrorApi可以这样访问

import React from 'react';
import { useApi, errorApiRef } from '@backstage/core-plugin-api';

export const MyComponent = () => {
const errorApi = useApi(errorApiRef);

// Signal to the app that something went wrong, and display the error to the user.
const handleError = error => {
errorApi.post(error);
};

// the rest of the component ...
};

注意ErrorApi这是因为errorApiRef类型为嵌入式,而使用应用程序就能推断出类型。

还要注意的是,使用实用程序编程接口并不局限于插件,Backstage的任何组件都可以使用,包括在@backstage/core-plugin-api唯一的要求是,它们必须在AppProvider在 React 树中。

提供应用程序接口

API 工厂

应用程序接口以ApiFactory实例,它封装了一个应用程序接口的实例化过程。 它是以下三项内容的集合:...ApiRef要实例化的 API 的名称、所有必要依赖项的列表,以及返回新 API 实例的工厂函数。

例如,默认情况下ApiFactoryErrorApi:

createApiFactory({
api: errorApiRef,
deps: { alertApi: alertApiRef },
factory: ({ alertApi }) => {
const errorApi = new ErrorAlerter(alertApi, new ErrorApiForwarder());
UnhandledErrorForwarder.forward(errorApi, { hidden: false });
return errorApi;
},
});

在这个例子中errorApiRef是我们的应用程序接口,它封装了ErrorApi类型警报 APIRef是我们的单一依赖项,我们将其命名为alertApi的实现,然后传递给工厂函数,工厂函数返回ErrorApi.

创建 ApiFactory函数是一个瘦包装器,可以实现 TypeScript 类型推断。 您可能会注意到,上面的示例中没有类型注解,这是因为我们能够从ApiRef的返回值。factory函数与api'sApiRef在这种情况下ErrorApi它还会匹配deps的参数,以及factory函数,再次使用嵌入在ApiRefs.

注册应用程序接口工厂

向 Backstage 应用程序添加实用程序编程接口的责任在于三个不同的地方:Backstage 核心库、应用程序中包含的每个插件以及应用程序本身。

核心应用程序接口

从Backstage核心库开始,它提供了所有核心应用程序接口的实现。 核心应用程序接口是由@backstage/core-plugin-api例如errorApiRefconfigigApiRef.

创建的任何应用程序都会加载核心应用程序接口。创建应用程序@backstage/core-plugin-api这意味着无需采取任何步骤即可在应用程序中包含这些 API。

插件 API

除了核心 API 之外,插件还可以定义和导出自己的 API。 在这样做的同时,它们通常还应该提供自己 API 的默认实现,例如catalog插件导出catalogApiRef还提供了一个默认ApiFactory使用CatalogClient插件提供的 API 工厂有一个限制:插件不得为核心 API 提供工厂,否则会导致应用程序无法启动。

插件通过apis选项的创建插件例如

export const techdocsPlugin = createPlugin({
id: 'techdocs',
apis: [
createApiFactory({
api: techdocsStorageApiRef,
deps: { configApi: configApiRef },
factory({ configApi }) {
return new TechDocsStorageApi({
apiOrigin: configApi.getString('techdocs.storageUrl'),
});
},
}),
],
});

应用程序接口

最后,应用程序本身是添加应用程序接口的最终点,也是在运行时加载哪些应用程序接口的最终决定权所在。 应用程序可以覆盖任何核心或插件应用程序接口的工厂,但配置、应用程序主题和身份应用程序接口除外。 这些都是静态应用程序接口,绑定于创建应用程序因此无法覆盖。

重写应用程序接口(API)对于想要根据环境调整行为的应用程序来说非常有用。 在某些情况下,插件还可能导出多个相同应用程序接口(API)的实现,它们各自对后端存储和周围环境等有不同的要求。

向应用程序提供应用程序接口的工作原理与插件相同:

const app = createApp({
apis: [
/* ApiFactories */
],
// ... other options
});

一种常见的模式是从apis.ts旁边App.tsx本软件仓库中的示例应用程序为例。

自定义实现实用程序应用接口

定义实用程序编程接口的自定义实现非常简单,只需导出一个类,该类的implements例如,目标应用程序接口:

export class IgnoringErrorApi implements ErrorApi {
post(error: ErrorApiError, context?: ErrorApiErrorContext) {
// ignore error
}
}

IgnoringErrorApi然后在应用程序中导入,并像这样连接起来:

const app = createApp({
apis: [
/* ApiFactories */
createApiFactory(errorApiRef, new IgnoringErrorApi()),

// OR
// If your API has dependencies, you use the object form
createApiFactory({
api: errorApiRef,
deps: { configApi: configApiRef },
factory({ configApi }) {
return new IgnoringErrorApi({
reportingUrl: configApi.getString('error.reportingUrl'),
});
},
}),
],
// ... other options
});

请注意,如果出现以下情况,上面一行将导致错误IgnoreErrorApi没有完全执行ErrorApi中嵌入的类型进行检查。errorApiRef在编译时

定义自定义实用程序接口

插件可以自由定义自己的实用程序编程接口,只需为程序编程接口定义 TypeScript 接口,并创建一个ApiRef使用创建 ApiRef@backstage/core-plugin-api还应确保提供至少一种应用程序接口的实现,并在创建插件.

自定义实用程序 API 可以是公开的,也可以是私有的,这取决于插件的选择。 私有 API 不会暴露外部 API 表面,因此可以对 API 进行破坏性更改,而不会影响插件的其他用户。 但是,如果公开 API,就会允许其他插件使用该 API,也可以让插件用户在应用程序中覆盖 API。 不过,保持公开 API 的向后兼容性很重要,否则可能会破坏正在使用插件的应用程序。

要公开应用程序接口,只需导出ApiRef和任何相关类型。 要使 API 私有化,只需避免导出ApiRef但仍要确保将默认工厂设置为创建插件.

私有 API 对于那些希望依赖 React 组件之外的其他 API,但又不必暴露整个 API 表面以进行维护的插件来说非常有用。 使用私有 API 时,可以使用typeof的类型参数传递给创建 ApiRef而公共 API 应始终定义一个单独的 TypeScript 接口类型。

插件可能会依赖其他插件的 API,无论是在 React 组件中还是作为 API 工厂的依赖关系。 但请确保不要在插件之间造成循环依赖关系。

架构

ApiRef实例在公用事业应用程序接口的消费者和生产者之间提供了一个间接点。 它允许插件和组件以一种类型安全的方式依赖于应用程序接口,而无需直接引用应用程序接口的具体实现。 应用程序在提供何种实现方面也有很大的灵活性。ApiRef因此,他们可以自由选择任何实施方式。

下图显示了不同的应用程序提供不同的执行方式FooApi.组件然后访问插件中的FooApi通过fooApiRef.

Figure showing the relationship between utility APIs, the apps that provide them, and the plugins that consume them

目前连接公用事业应用程序接口提供者和消费者的方法是通过 React 树,使用一个ApiProvider,它被添加到AppProviderApp未来可能会有更多不与 React 绑定的方式来实现这一点。 实用程序应用程序接口的一个设计目标就是不直接与 React 绑定。

实用程序编程接口(Utility APIs)提供的间接性也使得测试依赖于实用程序编程接口(APIs)的组件变得简单易行,并为插件提供了一个标准的通用开发环境。 带有模拟程序编程接口(APIs)实现的适当测试封装尚未准备就绪,但它将作为@backstage/test-utils它将提供应用程序接口的模拟变体,并提供额外的方法来断言组件与应用程序接口的交互。

插件的通用开发环境包含在@backstage/dev-utils`。其中导出的创建开发应用程序函数创建的应用程序已经包含了所有核心应用程序接口的实现。 与使用创建应用程序,创建开发应用程序使用自动依赖注入,这样就可以替换任何 API 的实现,并在该 API 的依赖项中反映出来。