refactor: 统一 target name/description 可空语义,前端展示 fallback 到 id
- schema: name/description 允许省略或显式 null,TypeBox Union([Null, String]) - 类型: RawTargetConfig/ResolvedTargetBase/子类型/StoredTarget/TargetStatus name 改为 string | null - checker resolve: name: t.name ?? null,不再 fallback 到 id - 语义校验: 拒绝空字符串和纯空白 name - SQLite: targets.name 列改为可空 TEXT - 前端: 新增 getTargetDisplayName(target) 展示 name ?? id - 测试: 覆盖 name/description null 全场景,查找改为按 id - 文档: 更新 README/DEVELOPMENT 和 6 个 openspec specs
This commit is contained in:
@@ -17,7 +17,11 @@
|
||||
|
||||
#### Scenario: targets 字段
|
||||
- **WHEN** Dashboard 响应包含 targets
|
||||
- **THEN** targets 数组中每个元素 SHALL 包含目标基本信息(id、name、description、group、type、target、interval)、latestCheck、stats、currentStreak 和 recentSamples 字段,其中 description 为 null 或字符串
|
||||
- **THEN** targets 数组中每个元素 SHALL 包含目标基本信息(id、name、description、group、type、target、interval)、latestCheck、stats、currentStreak 和 recentSamples 字段,其中 name 和 description 均为 null 或字符串
|
||||
|
||||
#### Scenario: target name 字段为 null
|
||||
- **WHEN** 某个 target 未配置 `name` 或显式配置 `name: null`
|
||||
- **THEN** Dashboard targets 响应中对应元素 SHALL 返回 `name: null`
|
||||
|
||||
#### Scenario: target description 字段
|
||||
- **WHEN** 某个 target 配置了 `description`
|
||||
@@ -109,7 +113,7 @@ Dashboard API SHALL 返回基于时间窗口计算的目标统计和连续状态
|
||||
|
||||
#### Scenario: TargetStatus 类型
|
||||
- **WHEN** 前后端共享 `TargetStatus` 类型
|
||||
- **THEN** 该类型 SHALL 包含目标基本信息字段(id、name、description、group、type、target、interval)、stats(totalChecks、upChecks、downChecks、availability)、currentStreak 和 recentSamples 字段,其中 description 类型为 null 或字符串
|
||||
- **THEN** 该类型 SHALL 包含目标基本信息字段(id、name、description、group、type、target、interval)、stats(totalChecks、upChecks、downChecks、availability)、currentStreak 和 recentSamples 字段,其中 name 和 description 类型均为 null 或字符串
|
||||
|
||||
#### Scenario: TargetMetricsResponse 类型
|
||||
- **WHEN** 前后端共享 `TargetMetricsResponse` 类型
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
## Requirements
|
||||
|
||||
### Requirement: YAML 配置文件格式
|
||||
系统 SHALL 支持通过 YAML 配置文件定义全部运行参数,包括 server 配置、runtime 配置、可选的 variables 段、checker 默认值和 typed target 列表(含可选 group 字段)。target MUST 使用 `id` 字段作为唯一标识符,MUST 使用 `type` 字段声明 checker 类型,SHALL 支持可选的 `name` 字段作为展示名称(缺省 fallback 到 id)。HTTP 领域字段 MUST 放在 `http` 分组,cmd 领域字段 MUST 放在 `cmd` 分组,db 领域字段 MUST 放在 `db` 分组。HTTP target 的 `http` 分组 SHALL 支持可选的 `ignoreSSL`(布尔值)和 `maxRedirects`(非负整数)字段。Db target 的 `db` 分组 SHALL 支持 `url`(必填)和 `query`(可选)字段。
|
||||
系统 SHALL 支持通过 YAML 配置文件定义全部运行参数,包括 server 配置、runtime 配置、可选的 variables 段、checker 默认值和 typed target 列表(含可选 group 字段)。target MUST 使用 `id` 字段作为唯一标识符,MUST 使用 `type` 字段声明 checker 类型,SHALL 支持可选的 `name` 字段作为展示名称元信息,SHALL 支持可选的 `description` 字段作为目标说明。`name` 和 `description` 均 SHALL 允许省略或显式配置为 `null`;省略或显式 null 时解析结果 SHALL 保留为 null。HTTP 领域字段 MUST 放在 `http` 分组,cmd 领域字段 MUST 放在 `cmd` 分组,db 领域字段 MUST 放在 `db` 分组。HTTP target 的 `http` 分组 SHALL 支持可选的 `ignoreSSL`(布尔值)和 `maxRedirects`(非负整数)字段。Db target 的 `db` 分组 SHALL 支持 `url`(必填)和 `query`(可选)字段。
|
||||
|
||||
`defaults.http` 分组 SHALL 仅支持 `headers`(可选)和 `maxBodyBytes`(可选)字段。`defaults.http` 分组 MUST NOT 支持 `method` 字段。
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
|
||||
#### Scenario: 最简 HTTP 配置文件解析
|
||||
- **WHEN** 系统读取只包含一个 `type: http` target(含 `id` 和 `http.url`)的 YAML 配置文件(省略 server、runtime、variables、defaults 和 expect)
|
||||
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段(host=127.0.0.1, port=3000, dir=./data, interval=30s, timeout=10s, runtime.maxConcurrentChecks=20, http.method=GET, http.maxBodyBytes=100MB, http.ignoreSSL=false, http.maxRedirects=0, group="default", name fallback 到 id)
|
||||
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段(host=127.0.0.1, port=3000, dir=./data, interval=30s, timeout=10s, runtime.maxConcurrentChecks=20, http.method=GET, http.maxBodyBytes=100MB, http.ignoreSSL=false, http.maxRedirects=0, group="default"),并保留 name=null、description=null
|
||||
|
||||
#### Scenario: 最简 cmd 配置文件解析
|
||||
- **WHEN** 系统读取只包含一个 `type: cmd` target(含 `id` 和 `cmd.exec`)的 YAML 配置文件
|
||||
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段(interval=30s, timeout=10s, cmd.cwd 为配置文件所在目录, cmd.maxOutputBytes=100MB, name fallback 到 id)
|
||||
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段(interval=30s, timeout=10s, cmd.cwd 为配置文件所在目录, cmd.maxOutputBytes=100MB),并保留 name=null、description=null
|
||||
|
||||
#### Scenario: per-target 配置覆盖全局默认值
|
||||
- **WHEN** 某个 target 指定 interval、timeout 或对应领域分组中的默认字段
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
#### Scenario: 最简 db 配置文件解析
|
||||
- **WHEN** 系统读取只包含一个 `type: db` target(含 `id` 和 `db.url`)的 YAML 配置文件
|
||||
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段(interval=30s, timeout=10s, group="default", name fallback 到 id)
|
||||
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段(interval=30s, timeout=10s, group="default"),并保留 name=null、description=null
|
||||
|
||||
#### Scenario: defaults.http.method 触发校验错误
|
||||
- **WHEN** 配置文件中出现 `defaults.http.method` 字段
|
||||
@@ -330,18 +330,30 @@
|
||||
- **THEN** 系统 SHALL 将默认值 `./data` 基于 configDir 解析为绝对路径
|
||||
|
||||
### Requirement: target 通用元信息字段约束
|
||||
系统 SHALL 在 YAML target 通用字段中支持 `description` 字段,并对 `id`、`name` 和 `description` 执行契约校验。`id` MUST 为 1 到 30 个字符,显式 `name` MUST 为 1 到 30 个字符,`description` MUST 不超过 500 个字符且 MAY 为空字符串。
|
||||
系统 SHALL 在 YAML target 通用字段中支持 `description` 字段,并对 `id`、`name` 和 `description` 执行契约校验。`id` MUST 为 1 到 30 个字符。`name` MUST 为 null 或 1 到 30 个字符的字符串,且语义校验 SHALL 拒绝仅包含空白字符的 name。`description` MUST 为 null 或不超过 500 个字符的字符串,且 MAY 为空字符串。
|
||||
|
||||
#### Scenario: description 字段解析
|
||||
- **WHEN** 系统读取包含 `description: "检查生产 API 健康状态"` 的 target
|
||||
- **THEN** 系统 SHALL 将该字段解析为 target 的目标说明
|
||||
|
||||
#### Scenario: name 为 null 通过校验
|
||||
- **WHEN** 系统读取包含 `name: null` 或省略 `name` 的 target
|
||||
- **THEN** 系统 SHALL 接受该配置
|
||||
|
||||
#### Scenario: name 仅包含空白字符报错
|
||||
- **WHEN** 系统读取包含 `name: " "` 的 target
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 name 不能为空白
|
||||
|
||||
#### Scenario: description 为空字符串
|
||||
- **WHEN** 系统读取包含 `description: ""` 的 target
|
||||
- **THEN** 系统 SHALL 接受该配置,且不触发长度错误
|
||||
|
||||
#### Scenario: description 为 null 通过校验
|
||||
- **WHEN** 系统读取包含 `description: null` 或省略 `description` 的 target
|
||||
- **THEN** 系统 SHALL 接受该配置
|
||||
|
||||
#### Scenario: description 类型非法
|
||||
- **WHEN** YAML 中某个 target 的 `description` 字段不是字符串
|
||||
- **WHEN** YAML 中某个 target 的 `description` 字段不是字符串也不是 null
|
||||
- **THEN** 系统 SHALL 以错误退出,提示 description 字段类型错误
|
||||
|
||||
#### Scenario: description 超过最大长度
|
||||
@@ -361,12 +373,12 @@
|
||||
- **THEN** 系统 SHALL 在契约校验阶段以错误退出,提示 description 字段长度错误
|
||||
|
||||
### Requirement: 配置 schema 导出包含 target 元信息约束
|
||||
系统 SHALL 在导出的 `probe-config.schema.json` 中包含 target `id`、`name` 和 `description` 的长度约束,用于编辑器提示和外部校验。
|
||||
系统 SHALL 在导出的 `probe-config.schema.json` 中包含 target `id`、`name` 和 `description` 的长度约束和可空类型,用于编辑器提示和外部校验。
|
||||
|
||||
#### Scenario: schema 导出 description
|
||||
- **WHEN** 系统导出 `probe-config.schema.json`
|
||||
- **THEN** target schema SHALL 包含可选的 `description` 字段,类型为 string,最大长度为 500
|
||||
- **THEN** target schema SHALL 包含可选的 `description` 字段,类型为 string 或 null,字符串最大长度为 500
|
||||
|
||||
#### Scenario: schema 导出 id 和 name 长度
|
||||
#### Scenario: schema 导出 id 和 name
|
||||
- **WHEN** 系统导出 `probe-config.schema.json`
|
||||
- **THEN** target schema SHALL 声明 `id` 的 minLength 为 1、maxLength 为 30,并声明 `name` 的 minLength 为 1、maxLength 为 30
|
||||
- **THEN** target schema SHALL 声明 `id` 的 minLength 为 1、maxLength 为 30,并声明 `name` 为可选字段,类型为 string 或 null,字符串的 minLength 为 1、maxLength 为 30
|
||||
|
||||
@@ -5,12 +5,16 @@
|
||||
## Requirements
|
||||
|
||||
### Requirement: SQLite 数据库初始化
|
||||
系统 SHALL 使用 Bun 内置 `bun:sqlite` 模块在配置的数据目录下创建 SQLite 数据库文件,并以 WAL 模式运行。数据库 schema MUST 支持 typed checker target 和结构化检查结果,targets 表 MUST 包含 `grp` 列存储分组信息。
|
||||
系统 SHALL 使用 Bun 内置 `bun:sqlite` 模块在配置的数据目录下创建 SQLite 数据库文件,并以 WAL 模式运行。数据库 schema MUST 支持 typed checker target 和结构化检查结果,targets 表 MUST 包含 `grp` 列存储分组信息,且 targets 表的 `name` 和 `description` 列 MUST 允许 NULL。
|
||||
|
||||
#### Scenario: 首次启动创建数据库
|
||||
- **WHEN** 指定的数据目录下不存在数据库文件
|
||||
- **THEN** 系统 SHALL 创建数据库文件并初始化 targets 表和 check_results 表,check_results 表包含 id(INTEGER PRIMARY KEY AUTOINCREMENT)、target_id(INTEGER NOT NULL)、timestamp(TEXT NOT NULL)、matched(INTEGER NOT NULL)、duration_ms(REAL)、status_detail(TEXT)、failure(TEXT),不包含 success 列
|
||||
|
||||
#### Scenario: targets name 列允许 NULL
|
||||
- **WHEN** 系统首次创建 targets 表
|
||||
- **THEN** targets.name 列 SHALL 允许存储 NULL
|
||||
|
||||
#### Scenario: 数据目录不存在
|
||||
- **WHEN** 配置的数据目录路径不存在
|
||||
- **THEN** 系统 SHALL 自动创建该目录
|
||||
@@ -26,20 +30,32 @@
|
||||
- **THEN** check_results 表的外键约束 SHALL 使用 `ON DELETE CASCADE`,确保删除目标时自动清理关联结果记录
|
||||
|
||||
### Requirement: targets 表同步
|
||||
系统 SHALL 在启动时将 YAML 配置中的目标列表同步到 SQLite targets 表,并持久化 target 类型、展示摘要、领域配置、调度配置、expect 配置、分组信息和目标说明。
|
||||
系统 SHALL 在启动时将 YAML 配置中的目标列表同步到 SQLite targets 表,并持久化 target 类型、展示名称元信息、展示摘要、领域配置、调度配置、expect 配置、分组信息和目标说明。
|
||||
|
||||
#### Scenario: 首次同步目标
|
||||
- **WHEN** 数据库为空且 YAML 中定义了 N 个 typed target
|
||||
- **THEN** 系统 SHALL 将所有目标插入 targets 表,包含 name、description、type、target、config、interval_ms、timeout_ms、expect 和 grp
|
||||
- **THEN** 系统 SHALL 将所有目标插入 targets 表,包含 name、description、type、target、config、interval_ms、timeout_ms、expect 和 grp,其中 name 和 description 均可为 NULL
|
||||
|
||||
#### Scenario: 配置变更后重新同步
|
||||
- **WHEN** YAML 配置发生变更(新增、删除或修改目标)后重启
|
||||
- **THEN** 系统 SHALL 根据 id 字段匹配:新增的插入、删除的移除、修改的更新(含 description 和 grp 字段)
|
||||
- **THEN** 系统 SHALL 根据 id 字段匹配:新增的插入、删除的移除、修改的更新(含 name、description 和 grp 字段)
|
||||
|
||||
#### Scenario: 未配置 name
|
||||
- **WHEN** YAML target 未配置 `name`
|
||||
- **THEN** targets 表 SHALL 将该目标的 name 存储为 NULL
|
||||
|
||||
#### Scenario: name 显式 null
|
||||
- **WHEN** YAML target 配置 `name: null`
|
||||
- **THEN** targets 表 SHALL 将该目标的 name 存储为 NULL
|
||||
|
||||
#### Scenario: 未配置 description
|
||||
- **WHEN** YAML target 未配置 `description`
|
||||
- **THEN** targets 表 SHALL 将该目标的 description 存储为 NULL
|
||||
|
||||
#### Scenario: description 显式 null
|
||||
- **WHEN** YAML target 配置 `description: null`
|
||||
- **THEN** targets 表 SHALL 将该目标的 description 存储为 NULL
|
||||
|
||||
### Requirement: check_results 表追加写入
|
||||
系统 SHALL 将每次检查结果追加写入 check_results 表,不更新或删除已有记录。
|
||||
|
||||
|
||||
@@ -13,7 +13,11 @@ Dashboard SHALL 在用户点击目标表格行后从右侧滑出 Drawer,展示
|
||||
|
||||
#### Scenario: Drawer 标题栏
|
||||
- **WHEN** Drawer 渲染
|
||||
- **THEN** 标题栏 SHALL 使用 TDesign Space 组件(align="center")布局,包含 StatusDot、目标名称(TDesign Typography.Text strong)和类型标签(TDesign Tag,直接显示 target.type 原始文本),以及内建关闭按钮。不使用内联 style 的 flex 布局
|
||||
- **THEN** 标题栏 SHALL 使用 TDesign Space 组件(align="center")布局,包含 StatusDot、目标展示名称(取值为 `target.name ?? target.id`,使用 TDesign Typography.Text strong)和类型标签(TDesign Tag,直接显示 target.type 原始文本),以及内建关闭按钮。不使用内联 style 的 flex 布局
|
||||
|
||||
#### Scenario: Drawer 标题栏 name 为 null
|
||||
- **WHEN** Drawer 渲染某个 `target.name` 为 null 的目标
|
||||
- **THEN** 标题栏 SHALL 显示该目标的 `target.id` 作为目标展示名称
|
||||
|
||||
#### Scenario: 关闭 Drawer
|
||||
- **WHEN** 用户点击关闭按钮、ESC 键或遮罩层
|
||||
|
||||
@@ -36,23 +36,39 @@
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 id 重复
|
||||
|
||||
### Requirement: target name 字段
|
||||
每个 target SHALL 支持可选的 `name` 字段作为展示名称。`name` 缺省时 SHALL fallback 到 `id` 的值作为展示名称。显式配置的 `name` 长度 MUST 为 1 到 30 个字符。`name` SHALL 支持变量替换。`name` MUST NOT 要求全局唯一。
|
||||
每个 target SHALL 支持可选的 `name` 字段作为展示名称元信息。`name` 缺省或显式配置为 `null` 时 SHALL 在配置解析、运行时模型、存储和 API 中保留为 null。前端展示目标名称时 SHALL 使用 `name ?? id`,但该 fallback MUST NOT 改变 target 本身的 name 值。显式配置的 `name` MUST 为长度 1 到 30 个字符的字符串,且去除首尾空白后 MUST 不为空。`name` SHALL 支持变量替换。`name` MUST NOT 要求全局唯一,MUST NOT 参与 target 唯一性判定。
|
||||
|
||||
#### Scenario: 配置 name
|
||||
- **WHEN** target 配置 `id: "api-health"` 和 `name: "API 健康检查"`
|
||||
- **THEN** 系统 SHALL 使用 "API 健康检查" 作为展示名称
|
||||
- **THEN** 系统 SHALL 在解析后保留 name 为 "API 健康检查"
|
||||
|
||||
#### Scenario: name 使用变量
|
||||
- **WHEN** target 配置 `name: "${env} API 健康检查"` 且 variables 中 `env: "生产"`
|
||||
- **THEN** 系统 SHALL 将展示名称解析为 "生产 API 健康检查"
|
||||
- **THEN** 系统 SHALL 将 name 解析为 "生产 API 健康检查"
|
||||
|
||||
#### Scenario: name 缺省 fallback 到 id
|
||||
#### Scenario: name 缺省保留为 null
|
||||
- **WHEN** target 配置 `id: "api-health"` 但未配置 `name`
|
||||
- **THEN** 系统 SHALL 使用 "api-health" 作为展示名称
|
||||
- **THEN** 系统 SHALL 在解析、存储和 API 响应中保留 name 为 null
|
||||
|
||||
#### Scenario: name 显式 null
|
||||
- **WHEN** target 配置 `id: "api-health"` 和 `name: null`
|
||||
- **THEN** 系统 SHALL 接受该配置,并在解析、存储和 API 响应中保留 name 为 null
|
||||
|
||||
#### Scenario: name 空 YAML 值
|
||||
- **WHEN** target 配置 `id: "api-health"` 且 `name:` 后不提供值
|
||||
- **THEN** 系统 SHALL 将该 name 按 null 处理,并接受该配置
|
||||
|
||||
#### Scenario: name 为 null 时展示 fallback
|
||||
- **WHEN** 前端展示 name 为 null 的 target
|
||||
- **THEN** 前端 SHALL 显示该 target 的 id 作为目标名称文案
|
||||
|
||||
#### Scenario: name 为空字符串报错
|
||||
- **WHEN** target 配置 `name: ""`
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 name 长度不合法
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 name 不能为空
|
||||
|
||||
#### Scenario: name 仅包含空白字符报错
|
||||
- **WHEN** target 配置 `name: " "`
|
||||
- **THEN** 系统 SHALL 以配置错误退出,提示 name 不能为空
|
||||
|
||||
#### Scenario: name 超过最大长度报错
|
||||
- **WHEN** target 配置超过 30 个字符的 `name`
|
||||
@@ -63,7 +79,7 @@
|
||||
- **THEN** 系统 SHALL 接受该配置,不报错(name 不要求全局唯一)
|
||||
|
||||
### Requirement: target description 字段
|
||||
每个 target SHALL 支持可选的 `description` 字段作为目标说明。`description` SHALL 支持变量替换。`description` 长度 MUST 不超过 500 个字符,且允许为空字符串。`description` MUST NOT 参与 target 唯一性判定。
|
||||
每个 target SHALL 支持可选的 `description` 字段作为目标说明。`description` 缺省或显式配置为 `null` 时 SHALL 在配置解析、运行时模型、存储和 API 中保留为 null。`description` SHALL 支持变量替换。`description` 字符串长度 MUST 不超过 500 个字符,且允许为空字符串。`description` MUST NOT 参与 target 唯一性判定。
|
||||
|
||||
#### Scenario: 配置 description
|
||||
- **WHEN** target 配置 `description: "检查生产 API 健康状态"`
|
||||
@@ -77,6 +93,14 @@
|
||||
- **WHEN** target 未配置 `description`
|
||||
- **THEN** 系统 SHALL 接受该配置,且目标说明为 null
|
||||
|
||||
#### Scenario: description 显式 null
|
||||
- **WHEN** target 配置 `description: null`
|
||||
- **THEN** 系统 SHALL 接受该配置,且目标说明为 null
|
||||
|
||||
#### Scenario: description 空 YAML 值
|
||||
- **WHEN** target 配置 `description:` 后不提供值
|
||||
- **THEN** 系统 SHALL 将该 description 按 null 处理,并接受该配置
|
||||
|
||||
#### Scenario: description 为空字符串
|
||||
- **WHEN** target 配置 `description: ""`
|
||||
- **THEN** 系统 SHALL 接受该配置,且目标说明为空字符串
|
||||
|
||||
@@ -44,7 +44,11 @@ Dashboard SHALL 按 group 字段将目标分组,每个分组使用 TDesign Car
|
||||
|
||||
#### Scenario: 名称列
|
||||
- **WHEN** 表格渲染
|
||||
- **THEN** 名称列 SHALL 显示目标名称,ellipsis 超长名称自动省略并 Tooltip 显示全名,且 SHALL NOT 支持排序
|
||||
- **THEN** 名称列 SHALL 显示目标展示名称,取值为 `target.name ?? target.id`,ellipsis 超长名称自动省略并 Tooltip 显示全名,且 SHALL NOT 支持排序
|
||||
|
||||
#### Scenario: name 为 null 的名称列
|
||||
- **WHEN** 表格渲染某个 `target.name` 为 null 的目标
|
||||
- **THEN** 名称列 SHALL 显示该目标的 `target.id`
|
||||
|
||||
#### Scenario: 类型列
|
||||
- **WHEN** 表格渲染
|
||||
|
||||
Reference in New Issue
Block a user