You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
同一个 URL、同一份 OpenAPI/discovery 描述,请求体契约却取决于部署里装了哪个插件。按 Prime Directive #12,这是「一个契约,N 种方言」;而且 ① 那个 'query' in body && 'cube' in body 的两可兼容本身就是 #12 说的「不要在消费端加宽容 fallback」的样本。
发错形状不报错,静默生成空 SELECT
这才是真正咬人的地方。真引擎收到带信封的 body 时,顶层没有 measures/dimensions,于是 ensureCube 推断出一个没有任何列的 cube,SQL 编译成:
POST /api/v1/analytics/query {"cube":"crm_account","query":{"measures":["count"]}}
→ 500 {"success":false,"error":{"message":"SELECT FROM \"crm_account\" - near \"FROM\": syntax error","code":500}}
注意 SELECT 和 FROM 之间是空的。没有任何一层说「你的请求体形状不对」,它一路走到驱动才因为 SQL 语法炸掉。
按 Prime Directive #10 记录。修 #3867(PR #3875)时踩到的,是我自己被骗过去的那个坑 —— 它让我在 #3867 原文里得出了「没读到数据」的错误结论,所以值得单独收口。
同一个端点,两种请求体
POST /api/v1/analytics/query的处理方由analytics服务槽决定,而这个槽有两个实现:① 降级 shim(
packages/metadata-protocol/src/plugin.ts)—— 收{cube, query}信封,并且明确做了两可兼容:② 真引擎(
AnalyticsServicePlugin,ctx.replaceService替换掉 ①)——AnalyticsService.query(query: AnalyticsQuery, context?),收裸AnalyticsQuery,即{cube, measures, dimensions, filters, …}全在顶层。没有信封兼容。所以:
{"cube":"x","measures":["count"]}{"cube":"x","query":{"measures":["count"]}}同一个 URL、同一份 OpenAPI/discovery 描述,请求体契约却取决于部署里装了哪个插件。按 Prime Directive #12,这是「一个契约,N 种方言」;而且 ① 那个
'query' in body && 'cube' in body的两可兼容本身就是 #12 说的「不要在消费端加宽容 fallback」的样本。发错形状不报错,静默生成空 SELECT
这才是真正咬人的地方。真引擎收到带信封的 body 时,顶层没有
measures/dimensions,于是ensureCube推断出一个没有任何列的 cube,SQL 编译成:注意
SELECT和FROM之间是空的。没有任何一层说「你的请求体形状不对」,它一路走到驱动才因为 SQL 语法炸掉。我在 #3867 的第一次实证就是这么发的 payload,拿到语法错误,于是写下「只报了语法错误,没有读到数据,不宜当成数据泄露断言」。用正确形状重测才发现能读到真实行 —— 结论差了一个量级。一个静默接受畸形输入的端点会把调查者引向错误结论,这本身就是修它的理由。
(PR #3875 之后这条响应变成
Internal server error,SQL 不再回显;但「不报形状错误」这一点没变,只是更难诊断了 —— 这也是为什么应该在入口把它挡掉,而不是靠错误信息去猜。)建议(未决策,留给维护者)
AnalyticsQuery为唯一契约,不符合就 400 并指出缺什么。AnalyticsQuery在@objectstack/spec/contracts里已有类型;按 Prime Directive Add metamodel interfaces for ObjectQL/ObjectUI contract #1 应该有对应的 Zod schema 作为唯一真源。这一条独立于下面两条,单独做就能消除「静默空 SELECT」。AnalyticsQuery为规范形状,信封走readAliasedConfig一类的声明式 shim(warn 一次、可 lint、可按计划移除),而不是留一个裸'query' in body三元。getMeta/generateSql两侧签名是否也有同类分歧 —— shim 的getMeta忽略cubeName参数、generateSql直接返回{sql:null},与真引擎的行为差别是否已在 discovery 的degraded自述里如实反映。倾向先做 1(小、独立、立刻消除静默失败),2 排在其后。
关联:#3867、PR #3875、#3770、ADR-0021、ADR-0076 D10/D12。