覆盖 1713 条路径 / 2269 个操作 / 3116 个 schema。对标 OpenAPI 3、REST/RFC 9110、Google AIP、Stripe & GitHub 接口规范。
specs/omada_api.jsonOmada Open API v0.13.0.1两套并行的资源树:单租户控制器 /openapi/v1/{omadacId}/...(1609)与 MSP 多租户 /openapi/v1/msp/{mspId}/...(104)。版本 v1 主导,v2/v3 零星补丁。
lan-networks
三版本共存;clients 双版本。
47 个 operationId 还把版本号塞在名字里(modifyBtIbeaconConfigV2)。
与 OpenAPI / HTTP 规范直接冲突。导致 SDK 必须手写补丁、第三方代码生成器/Postman/Stoplight 无法正确导入。
200,零非-2xx 状态码{"200"}(命中 2269),错误一律以 HTTP 200 + body errorCode != 0 表达。*/**/* 一个媒体类型。application/json;*/* 让 SDK 生成器无法挑 schema,客户端必须凭直觉信任服务器实际返回的是 JSON。components.securitySchemes 为空,所有 op 无 securityjq .components.securitySchemes ⇒ {};逐 op .security 全 null。flows、scopes,否则 SDK 生成器、Postman 都不知道如何鉴权。OAuthTokenStore 与 buildClient。{errorCode, msg} 取代 HTTP 语义OperationResponseWithoutResult:{ errorCode: int32, msg: string }。业务错通过 errorCode != 0 表达,HTTP 永远 200。application/problem+json 是当代默认。GitHub、Stripe 在 4xx 上返回结构化 error。信封风格只有早期阿里云/腾讯云 API 采用,且都在向标准状态码迁移。servers[0].url 是 http://http://use1-omada-northbound.tplinkcloud.com — 唯一一条 server,且仅 us-east-1 区域。assertSecureUrl 强制 https — spec 本身仍是错的。不破坏功能但增加学习成本与样板代码量,长期累积形成"两套以上习惯共存"。
getGridAllClients → POST /openapi/v2/.../clients、getTopologyClientsByDevices、getDevice5MinStatistic。GET ops 命名为 list/query 等的 59 个一致性问题。_search、Google AIP-136 用 :search 自定义动词命名以保留语义信号。/cmd/ 子树承担动作型写操作/sites/{siteId}/cmd/devices/batch-adopt、/cmd/aps/move、/cmd/mlag/{mlagId}/reboot。:customVerb(devices:batchAdopt)。/cmd/ 与 REST /{collection}/{id} 形成两套并行习惯,客户端要记两套路由表。batch、batch-adopt、multi-add、batchModifyApVlanConfig、batchSetPortStatusForGivenPorts_1(54 个 batch 前缀 + 2 个 multi-add 路径)。_1 后缀强烈暗示历史命名冲突未清理。batchXxx 或 :batchCreate。customer-roles/audit-logs(kebab) vs allowApp/allTabs/categoryAppInfo(camel) vs 5Min/ipsec_failovers(数字开头 / snake_case)。"AP Info"、"AP MAC list"、" BatchUnbindSites"(首字符空格)、"OperationResponseListActivity Records Of A Client's Single Connections"。_、%20 或直接报错。质量事故级问题。sorts.name=asc、filters.wireless=true、filters.timeStart=…、filters.radioId=0 共 80+ 个。style: deepObject 不兼容。Stripe 用 expand[]、GitHub 用扁平 sort=/order=、Google 用 filter= AIP-160 表达式。page+pageSize 双 requiredpage 与 pageSize 同时传,page 1-based。page 默认 1;Stripe limit 默认 10;Google AIP-158 用 page_token+page_size。强制双必填只增加样板。/openapi/v1|v2/{omadacId}/sites/{siteId}/clients 双版本,lan-networks 三版本。Deprecation/Sunset header(RFC 8594)、缺 x-replaced-by 扩展。79 个 deprecated 操作没有指向继任者的链接。V2 后缀/v2/ 和名字里的 V2,如 modifyBtIbeaconConfigV2 → /openapi/v1/.../config/{id}(URL 是 v1,名字是 V2)。_1、PascalCase、5Min。ids[] 放进 DELETE body。fetch() 会丢弃。Stripe/GitHub 用 POST :batchDelete 或多次 DELETE。Idempotency-Key 头参数X-RateLimit-*、Retry-After。本仓库 parseRetryAfter 修复过"过去时间 → thundering herd" — 反映 spec 本就不规定。examples / x- 业务扩展x-order(排序用),无 examples、x-stability、x-tier — SDK 智能补全无素材。/openapi/v1/msp/{mspId}/customers/{customerId}/sites/{siteId}/olts/{deviceMac}/l2-feature/eth-port/port/unit1/list。MSP 把租户/客户/站点都打进 URL,单租户 mode 又重复一遍。/openapi/v1/{omadacId}/... 几乎全在 /openapi/v1/msp/{mspId}/customers/{customerId}/sites/{siteId}/... 下重复一遍。同一维度上,Omada 当前状态 vs Stripe / GitHub / Google AIP 的做法。
| 维度 | Omada | Stripe | GitHub | Google AIP | 差距 |
|---|---|---|---|---|---|
| HTTP 状态码 | 永远 200 | 标准 2xx/4xx/5xx | 标准 | 标准 + google.rpc.Status |
最大 |
| 错误格式 | {errorCode, msg} |
RFC 7807-like + type |
message + documentation_url |
google.rpc.Status |
大 |
| 资源命名 | 复数 + camelCase 混 kebab 混 snake | kebab,复数 | kebab,复数 | snake_case,复数 | 中 |
| 集合 vs 自定义动作 | /cmd/... + 路径动词 + POST-as-GET 三种共存 |
POST /resource/:action |
POST /:action |
:customVerb |
中 |
| 分页 | offset,双 required | cursor (starting_after) |
offset 或 cursor | page_token + page_size |
中 |
| 过滤 | filters.x= dot-notation |
created[gte]= |
flat query | filter= AIP-160 EBNF |
中 |
| 鉴权声明 | spec 空 | OAuth2/Bearer 完整 | OAuth2/PAT 完整 | OAuth2 完整 | 直接缺失 |
| 弃用治理 | 79 处 deprecated: true,零继任者 |
Sunset + changelog |
X-GitHub-Media-Type + nbf |
resource_reference |
中 |
| 幂等 | 无 | Idempotency-Key |
部分支持 | ETag / If-Match |
大 |
| 媒体类型 | */* |
application/json |
application/vnd.github+json |
application/json |
大 |
| TLS | spec http:// |
https-only | https-only | https-only | 大 |
阅读 packages/sdk/src/client 与 TODO.md 的修复记录,可以反推:很多 P0/P1 都是被工程外科手术地绕过去的。
assertSecureUrl 强制 https — 因为 spec server 是 http://。expiresIn ≤ 0、Retry-After 过去时间挡掉 — 因为 spec 不约束这些边界。paginate() 硬上限 10,000 页 — 因为分页参数永远要求 page+pageSize,又没有 nextPageToken。onError + flush — 因为没有标准化错误模型,业务 errorCode 都靠你审计。api-diff --fail-on-breaking — 因为上游 79 deprecated 没有继任者声明。errorCode/msg 转 RFC 7807 application/problem+json。components.securitySchemes 写出 OAuth2 client-credentials + scopes,每 op 标注 security。content 从 */* 改为 application/json。servers[].url 改 https,补齐多区域。/cmd/ 与 POST-as-GET 收敛到 :customVerb 或 /{collection}/{id}/{action}。pageToken/nextPageToken,pageSize 可省。filter= AIP-160 或扁平参数,废弃 filters.*。POST :batchDelete。x-replaced-by + Sunset。Idempotency-Key header;two-phase confirm-token 可借此 anchor。X-RateLimit-{Limit,Remaining,Reset}。examples + x-stability: stable|beta|deprecated。X-Omada-Tenant header 合并,路径深度 ≤ 8。m6-auth-research-questions.md。get 开头」「新增 DELETE 带 body」「响应只有 200」等可机器化规范回归。pnpm generate 后做一次重命名映射,避免生成的 TS 类型名包含 $ / _20 这种转义。