@kerin/zentao-api
v1.3.5
Published
Browser and Node.js SDK for ZenTao API
Readme
@kerin/zentao-api
Browser & Node.js SDK for ZenTao (禅道) API v2.
@kerin/zentao-api 是一个面向禅道 API v2 的轻量 JavaScript/TypeScript SDK,可用于 Node.js 18+、浏览器打包工具以及 CDN/script 标签场景。本包位于 zentao monorepo 的 packages/zentao-api。
安装 / Install
npm install @kerin/zentao-apiBun:
bun add @kerin/zentao-api快速开始 / Quick Start
创建客户端
import { ZentaoClient } from '@kerin/zentao-api';
const client = new ZentaoClient({
baseUrl: 'https://zentao.example.com',
token: 'your-token',
});
const products = await client.get('/products');baseUrl 是禅道站点根地址。SDK 会在内部追加 /api.php/v2。
账号密码登录
如果还没有 token,可以使用账号密码登录:
const client = new ZentaoClient('https://zentao.example.com');
const token = await client.login('admin', 'password');全局客户端与模块请求
import { ZentaoClient, request, setGlobalOptions } from '@kerin/zentao-api';
ZentaoClient.init({
baseUrl: 'https://zentao.example.com',
token: 'your-token',
});
setGlobalOptions({ recPerPage: '50' });
const result = await request('product/list', {});请求名支持三种写法:
await request('product'); // 默认调用 product/list
await request('product/list'); // 显式调用模块动作
await request('product/1'); // 详情快捷写法,等价于 product/get + id=1单次调用的选项会覆盖全局选项:
const result = await request('bug/list', { product: 1 }, { limit: '10' });从本地 Profile 恢复
SDK 支持将登录信息持久化到本地(Node.js: ~/.config/zentao/zentao.json,浏览器: localStorage),后续可直接恢复客户端:
const client = await ZentaoClient.fromProfile();
// 或指定 profile key
const client = await ZentaoClient.fromProfile('admin@https://zentao.example.com');API 概览
ZentaoClient
| 方法 | 说明 |
|------|------|
| client.get<T>(path) | GET 请求 |
| client.post<T>(path, body) | POST 请求 |
| client.put<T>(path, body) | PUT 请求 |
| client.delete<T>(path) | DELETE 请求 |
| client.login(account, password) | 账号密码登录,返回 token |
| client.request(path, options?) | 通用请求(底层方法) |
| ZentaoClient.init(options) | 创建全局单例客户端 |
| ZentaoClient.create(options) | 工厂方法创建客户端 |
| ZentaoClient.fromProfile(key?) | 从持久化 profile 恢复客户端 |
模块请求
| 函数 | 说明 |
|------|------|
| request(name, params?, options?) | 按 "module"、"module/action" 或 "module/<objectID>" 调用已注册模块 |
| defineModules(modules, options?) | 注册或扩展模块定义 |
| defineModuleActions(module, actions) | 为已有模块追加或替换动作 |
| extendModuleAction(module, action, patch) | 深度合并补丁,或用回调改写已有动作 |
| getModule(name) | 获取模块定义 |
| getModuleAction(module, action) | 获取指定动作定义 |
| getModuleNames() | 获取所有已注册模块名 |
| setGlobalOptions(options) | 设置全局默认选项 |
| getGlobalOptions() | 获取当前全局选项 |
错误处理
SDK 所有传输层错误均通过 ZentaoError 抛出,包含稳定的错误码:
import { ZentaoError } from '@kerin/zentao-api';
try {
await client.get('/products');
} catch (error) {
if (error instanceof ZentaoError) {
console.error(error.code); // e.g. 'E_HTTP_ERROR', 'E_TIMEOUT', 'E_NETWORK_ERROR'
console.error(error.message);
}
}注意:服务端返回
{ status: "fail" }时 SDK 默认不会抛出异常,按原始响应内容返回。需要把业务失败转为异常时,可在单次请求或全局选项中启用throwOnFail。HTTP/网络/超时等传输层错误始终会抛出ZentaoError。
扩展模块
生成的模块定义来自 scripts/update-registry.ts(输入 data/zentao-openapi.json)。你可以在调用 request() 前扩展模块,或新增、替换动作。
新增模块
import { defineModules } from '@kerin/zentao-api';
defineModules({
name: 'custom',
actions: [
{
name: 'list',
type: 'list',
method: 'GET',
path: '/custom',
resultType: 'list',
resultGetter: 'items',
},
],
});为已有模块追加动作
import { defineModuleActions } from '@kerin/zentao-api';
defineModuleActions('bug', {
name: 'archive',
type: 'action',
method: 'PUT',
path: '/bugs/{bugID}/archive',
pathParams: { bugID: 'Bug ID' },
resultType: 'text',
});微调已有动作
只想改动作的个别字段(而不是整体替换)时,用 extendModuleAction。传入补丁对象会与原动作深度合并:普通对象递归合并,数组及其他值整体替换,undefined 的键会被忽略。
import { extendModuleAction } from '@kerin/zentao-api';
// 改写 task/list 的 URL
extendModuleAction('task', 'list', {
path: '/executions/{executionID}/tasks',
pathParams: { executionID: '执行ID' },
});需要基于现有定义做条件改写时,可传入回调。回调收到当前动作的深克隆,返回值作为完整动作定义直接取代原动作(不再合并):
extendModuleAction('execution', 'create', (action) => {
const required = action.requestBody!.schema?.required;
if (Array.isArray(required) && !required.includes('products')) {
required.push('products');
}
return action;
});合并与替换
同名模块默认合并定义:同名动作会替换,未知动作会追加。如需整体替换模块,传入 { replace: true }:
defineModules(myModule, { replace: true });如果扩展定义拆分在多个文件中,请在应用启动入口中显式导入这些文件,确保它们在调用 request() 前完成注册。
TypeScript 支持
SDK 提供完整的 TypeScript 类型定义,所有公共类型均可直接导入:
import type {
ZentaoClientOptions,
ModuleDefinition,
ModuleAction,
ResponseData,
RequestOptions,
} from '@kerin/zentao-api';浏览器
浏览器打包工具可以正常导入这个包:
import { ZentaoClient } from '@kerin/zentao-api';
// 或显式 browser 子路径(与主入口等价,不注入全局变量)
import { ZentaoClient } from '@kerin/zentao-api/browser';如果使用 script 标签,请使用浏览器 IIFE 构建包,并从 window.ZentaoAPI 读取 API:
<script src="https://cdn.jsdelivr.net/npm/@kerin/zentao-api@latest/dist/browser/zentao-api.global.js"></script>
<script>
console.log(window.ZentaoAPI.VERSION, window.ZentaoAPI.BUILD);
const client = new window.ZentaoAPI.ZentaoClient('https://zentao.example.com');
</script>也可通过子路径 @kerin/zentao-api/browser/global 引用同一 IIFE 产物。
CORS:浏览器直接请求要求禅道服务器允许 CORS。浏览器代码也会把 token 暴露给前端;如果这不可接受,请使用后端代理。
TLS:
insecureTLS 选项仅适用于 Node.js,在浏览器运行时会抛出错误。
测试
本 monorepo 开发依赖管理仅使用 Bun(在仓根 bun install)。请勿使用 npm / pnpm / yarn 安装 workspace 依赖,以免生成其它 lockfile。
在本包目录下:
bun test # 单元测试
bun run test:coverage # 含覆盖率的单元测试
bun run check # 完整 CI:测试 + 类型检查 + 注册表 + 构建 + 冒烟测试真实环境测试
真实环境测试需要连接到运行中的禅道实例,不包含在默认 bun test 中:
bun run test:real
bun run test:real -- --keep-test-data # 保留临时数据以便手动检查测试会优先读取 .env.local,如果不存在则读取 env.local。支持的环境变量:
| 变量 | 说明 | 默认值 |
|------|------|--------|
| ZENTAO_URL | 禅道站点地址 | (必填) |
| ZENTAO_ACCOUNT | 登录账号 | (必填) |
| ZENTAO_PASSWORD | 登录密码 | (必填) |
| ZENTAO_TOKEN | 直接提供 Token(替代账号密码) | — |
| ZENTAO_REVIEWER | 需求评审人(未设置时复用 ZENTAO_ACCOUNT) | — |
| ZENTAO_KEEP_TEST_DATA | 保留临时测试数据 | false |
| ZENTAO_TIMEOUT | 请求超时(ms) | 30000 |
| ZENTAO_INSECURE | 跳过 TLS 证书验证 | false |
项目结构
packages/zentao-api/
├── src/
│ ├── client/ # ZentaoClient 核心实现
│ ├── modules/ # 模块注册表
│ │ ├── generated.ts # OpenAPI 自动生成,勿手动编辑
│ │ ├── override.ts # 内置手写补丁
│ │ ├── define.ts # defineModules / defineModuleActions / extendModuleAction
│ │ ├── query.ts # getModule / getModuleNames 等只读 API
│ │ ├── registry-store.ts
│ │ ├── registry.ts # barrel
│ │ └── resolve.ts # 路径模板与参数解析
│ ├── request/ # 高阶 request()
│ ├── profiles/ # 本地 profile 持久化
│ ├── misc/ # 错误、全局选项、环境检测
│ ├── types/ # TypeScript 类型
│ ├── utils/ # 通用工具
│ ├── browser.ts # browser 子路径入口(不注入全局)
│ ├── browser-global.ts # IIFE 入口 → window.ZentaoAPI
│ └── index.ts # 公共 API 入口
├── scripts/ # 构建、注册表生成、文档、冒烟
├── tests/
├── data/ # OpenAPI 规范
├── docs/ # VitePress(guide / reference / zentao-api)
└── dist/ # 构建产物文档站点
bun run docs:dev # 生成本地文档并预览
bun run docs:generate # 刷新 typedoc reference + 模块 API 页
bun run docs:build # 构建静态站到 docs/.vitepress/distdocs/guide/— 手写指南docs/reference/— typedoc 自动生成,勿手改docs/zentao-api/— 从模块注册表自动生成,勿手改
贡献
欢迎贡献代码!请确保提交前通过完整检查:
bun run check在 monorepo 根目录也可用 bun run build / bun run test 做跨包验证。CLI 包 @kerin/zentao-cli 依赖本 SDK 的 dist,改源码后需重建本包。
