首先插件的功能是私有化部署的功能, 只能在私有化部署的 ONES 上能安装和使用插件。
安装最新版本 Node.js 的稳定版, 配置 ONES 的私有 npm 仓库:
$ npm config set registry=https://registry.npmjs.org/
$ npm config set @ones:registry=https://npm.partner.ones.cn/registry/
$ npm config set @ones-op:registry=https://npm.partner.ones.cn/registry/
这里本来想先安装 Homebrew 后通过 brew install n
安装 n 工具, 再使用 sudo n 16.13.0
安装 v16.13.0 版本的 Node.js 环境, 但是出现 github.com port 443: Operation timed out
异常, 尝试过更换中科院的镜像地址和 Leensa 网络代理工具均无效时, 放弃这个方案。
下载 cmake-3.22.2-macos-universal.dmg 安装 CMake 工具, 然后通过 cmake --version
检查是否安装成功。
这里遇到了 Error: Running Homebrew as root is extremely dangerous and no longer supported.
异常, 通过下面的命令解决:
$ sudo chown -R `whoami` /usr/local/Homebrew/
$ sudo chown -R $(whoami) $(brew --prefix)/*
$ sudo mkdir /usr/local/Frameworks
$ sudo chown -R `whoami` /usr/local/Frameworks/
使用 npm install -g @ones/cli
安装 ONES CLI 脚手架工具, 再使用 ones --version
验证是否正确安装, 以后的升级用 npm update -g @ones/cli
命令来完成。
插件在作用范围上, 分为 组织级别 和 团队级别 两种;在功能范围上, 分为 业务插件 、账号管理与通知 和 托管独立服务 三种。
新建一个目录作为插件工程的根目录, 进入目录执行 ones create -d
命令创建一个插件工程, 其中的 -d
选项表示使用默认的工程创建预设配置。这样创建的是 团队级别 的插件, 可以用 --plugin-type organization
选项表示创建 组织级别 的插件。
插件项目的目录 (ONES CLI 创建的插件工程目录结构) 生成后, 使用 npx op packup Demo
命令对插件进行打包, 模式 (no-modify-无修改 | major-主版本 | minor-次版本 | patch-修复版本) 选择 no-modify
无修改。
打包完成后, 工程根目录中就可以看到 Demo.opk 这个 opk 包, 这个是 团队级别 的插件, 在 “配置中心 -> 插件管理” 选择 Demo.opk 文件完成安装, 如果是 组织级别 的则在 “组织管理 -> 插件管理” 页面安装。
执行 npx op login
命令在私有化部署环境中获取用户凭证的参数 (执行前需要在 “配置中心 -> 插件管理” 删除掉之前安装), 并存储到 config/local.yaml
中。命令要求输入的参数 base URL 是私有化 ONES 平台的域名地址 (例如 “https://xxxx/”), 参数 host URL 是私有化 ONES 平台的 TCP 主机地址和 9006
端口号 (例如 “tcp://127.0.0.1:9006”), 还有就是登录私有化 ONES 平台的账号密码, 以及选择进入的团队。
生成 config/local.yaml
后, 执行 npx op invoke run
启动本地调试, 这个命令会为插件工程的前端部分 (/web
) 启动一个热更新的开发服务器, 第一次启动后控制台会返回 instance_uuid
值, 这个值是插件被安装到具体环境时动态生成的实例 uuid 值。
现在 “配置中心 -> 插件管理” 里刷新, 可以看到插件已经被安装了, 中断前面的 npx op invoke run
进程, 再执行 npx op invoke clear
就可以卸载插件, 卸载完成后再中断当前进程, 这个就是插件的调试过程。
修改插件的配置文件 (config/plugin.yaml
) 后需要卸载插件后重新安装, 即重新执行 npx op invoke run
和 npx op invoke clear
命令。
插槽是纯前端的能力, 存放于 /web/src/modules/
目录下以插槽模块命名的目录中, 插槽模块相当于一个独立的 React 应用, 支持 TypeScript 语言。
以全局的顶栏公告插槽 ones:global:banner 为例, 执行 npx op add module
命令并选择 ones:global:banner 插槽, 输入 banner
或其他标题, 就完成插槽的创建, 在 /config/plugin.yaml
文件的最后多了一个 modules
字段, 在这个字段下有一个 entry
字段, 对应的是 /web/src/{entry}
目录下的文件路径, 这个文件目录下包含 index.tsx
与 index.css
两个文件。
插槽模块目录下的 index.css
文件是样式代码, 自动生成的 #ones-mf-root {}
是插槽模块的根元素, 插槽支持 PostCSS 框架, 例如可以创建一个单独的样式文件 banner.css
并输入下面代码:
.banner {
width: 100%;
}
然后在 index.css
中引入这个 banner.css
文件:
@import 'banner.css';
#ones-mf-root {}
保存以上代码后, 在 ONES 平台页面的顶部就可以看到插槽模块的效果, 插件在打包时仅执行 .ts
和 .tsx
文件的转译, 插件模块的打包以模块内的 index.tsx
文件为入口, 这个文件里的
能力有 基础能力-平台开放能力 和 业务能力-应用开放能力 两种, 以 简单登录 能力为例, 执行 npx op add ability
指令向插件新增开放能力, 并选择 simple-auth 能力, 就完成能力的创建, 在 /config/plugin.yaml
文件的最后多了一个 abilities
字段, 同时在 /backend/src
目录下, 生成了 index.ts
和 simple-auth.ts
两个文件。
大概得出一个小结, 插槽是前端的显示, 能力是后端的实现, 插槽和能力如何进行关联需要更深入的了解。
首先是前端请求, 进入插件工程的 /web
目录执行 npm install @ones-op/fetch
命令进行依赖安装, 使用 ONES CLI 创建工程的时默认帮注册的 API 即 hello
接口, 在 plugin.yaml
中有一个 apis
字段, 它的 url
字段是 /hello
, 对应的是在 /backend/src/index.ts
里有一个 export async function hello(request: PluginRequest)
函数, 这个就是 API 的实现。这样就可以在插件中使用 OPFetch
来请求这个接口。
直接用 Postman 请求时需要带上 Ones-Check-Point、Ones-Plugin-Id、Ones-Check-Id 三个请求头, 插件 ID 在 “配置中心 -> 插件管理 -> 插件详情” 的 URL 里看, 团队 ID 在 “配置中心 -> 团队配置 -> 团队消息” 的 URL 里看, 然后用 POST 请求 https://xxxx/project/api/project/hello
地址就可以获得响应。
然后是后端请求, 进入插件工程的 /backend
目录执行 npm install @ones-op/node-fetch
命令进行依赖安装, 在 /backend/src/index.ts
文件里修改代码, 用 const projects = await fetchONES
就可以调用接口请求。
在插件里可以获取平台的 全局数据 和 特定上下文数据 两种, 进入插件工程的 /web
目录执行 npm install @ones-op/store
命令进行依赖安装, 然后就可以通过下面代码获取当前团队信息:
import { useTeamInfo } from '@ones-op/store'
const { uuid, name } = useTeamInfo()
把上面代码加到插槽模块里, 这样就可以在前面的插槽模块里显示当前团队信息。
平台提供了一套事件通信库, 通过进入插件工程的 /web
目录执行 npm install @ones-op/event
命令进行依赖安装, 就可以使用一套事件通信库:
大概实现的通过插槽模块里调用事件通信库, 来使用平台提供的事件, 例如在 “创建新用户时” 调用 “触发成员列表刷新”、“访问自定义的项目概率插件” 时 “激活全局进度管理器” 等实现。
插件的生命周期是指插件在 ONES 私有环境中从上传到卸载整个阶段的完整过程, 包括 “未安装”、“未启用”、“启用” 三个过程, 而在这个过程中伴随着一系列的生命周期事件的钩子函数, 这些钩子函数在插件工程初始化后就包含在 /backend/src/index.ts
文件中, 直接看注释就可以理解其含义。
在插件工程的根目录下执行 npx op packup
命令就可以打包插件了。
插件支持在 plugin.yaml
文件下的 config
字段添加自定义配置项, 配置项分3个类型, 通过配置项字段 type
区分:
type: Input
# 单行输入框type: TextArea
# 多行输入框type: Password
# 密码输入框除了 type
还有另外4个配置项字段, 其中必填的有1个:
配置项字段 | 说明 | 类型 |
---|---|---|
key | 唯一标识 | string |
其中必填的有3个:
配置项字段 | 说明 | 类型 | 默认值 |
---|---|---|---|
required | 字段是否必填 | boolean | false |
label | 字段别名 | string | 取 key 字段的值 |
value | 字段默认值 | string | 无 |
还可以在详情页面里新建选项卡, 在插件工程目录下执行 npx op add module
命令, 选择创建 settings
插槽的模块, 再输入一个标签页标题, 这样在插件详情页, 就可以在插件详情页面看到新的标签页。
接口注册属于插件能力, 插件能力允许插件注册 插件级别 和 团队级别 两种级别的接口, 它们在注册时的区别在于在 config/plugin.yaml
文件的 apis
字段的 type
子字段, 片段type: addition
表示 插件级别 的接口, 片段type: external
表示 团队级别 的接口, 它们的访问路径也不同:
https://xxx/project/api/project/hello
访问https://xxx/project/api/project/team/:teamUUID/hello1
访问除了文档中提供的内容, 还有另外一种区分方法:
/project/api/project/
的为全局接口/project/api/project/team/:teamUUID/
的为团队接口/project/api/project/organization/:organizationUUID/
的为组织接口此外 apis
字段还有一个 scope 子字段, 用于定义当前接口的路由是 project
还是 wiki
路径:
scope: project
时URL为 /project/api/project/
路径scope: wiki
时URL为 /wiki/api/wiki/
路径还有一些特殊情况, 例如 https://xxx/wiki/api/project/organization/:organizationUUID/user_list_filter_rules
接口, 就是 /wiki/api/project/
这样的路径。
接口劫持可以替换平台已有的接口, 也可以对已有的接口请求前后添加 前置处理 和 后置处理 逻辑, 实现接口劫持同样是在 config/plugin.yaml
文件 apis
字段的 type
子字段里:
type: replace
时为接口替换, 即重写接口type: prefix
时为前置劫持, 即前置处理type: suffix
时为后置劫持, 即后置处理因为是对已有接口做处, 所以填写的 url 必须跟访问 ONES API 的 url 保持一致, 同时要确认被操作接口是 POST 请求还是 GET 请求, 还要注意请求参数和响应内容。此外 apis
字段还有一个 query
子字段, 用于控制接口劫持的前置条件, 只有当请求值包含某些参数时才劫持接口。
第一个 场景:系统顶栏右侧组件 例子中, 是通过 permission
字段和 permission: component_permissions
为 插槽模块 直接配置一个独立的权限管理:
service:
permission:
- name: Component permissions
field: component_permissions
desc: System sider permissions
modules:
- id: ones-layout-header-action-new-LZrd
title: layoutHeaderAction
moduleType: ones:layout:header:action:new
enableMemoryRouter: true
entry: modules/ones-layout-header-action-new-LZrd/index.html
permission: component_permissions
第二个 场景:支持权限上下文的自定义迭代组件 例子中, 先使用 npx op add ability
添加了 custom-permission
这个 插件能力 并将其与 插槽模块 关联, 实现权限控制。