Skip to content

fix: align all HttpDispatcher analytics calls with IAnalyticsService contract#872

Merged
hotlong merged 4 commits intomainfrom
copilot/fix-analytics-service-method-naming
Mar 4, 2026
Merged

fix: align all HttpDispatcher analytics calls with IAnalyticsService contract#872
hotlong merged 4 commits intomainfrom
copilot/fix-analytics-service-method-naming

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 4, 2026

  • Fix http-dispatcher.ts: rename getMetadatagetMeta to match IAnalyticsService contract
  • Fix http-dispatcher.ts: remove extra { request: context.request } from query() and generateSql() calls
  • Fix http-dispatcher.ts: prefix unused context param with _ to fix TS6133 build error (follows existing _context convention in handleI18n/handleUi)
  • Fix http-dispatcher.test.ts: rename mock method getMetadatagetMeta
  • Update ROADMAP.md with fix record
  • Build passes — no TypeScript errors
  • All 85 runtime tests pass
Original prompt

This section details on the original issue you should resolve

<issue_title>[BUG] Analytics服务接口方法命名不一致,导致端点功能失效</issue_title>
<issue_description>## 问题概述

Spec项目中 IAnalyticsService 合约接口定义为 getMeta(cubeName?: string),但在 packages/runtime/src/http-dispatcher.ts (HttpDispatcher) 的实现和相关测试中,调用的是 getMetadata

  • 这导致生产环境 HttpDispatcher 调用 /analytics/meta 时无法正常触发服务实现(MemoryAnalyticsService/其他实现均只提供 getMeta 方法),最终 analytics/meta 端点的功能实际不可用,也影响了外部如 ObjectUI 的集成和适配复杂度。

影响范围

  • 所有通过 /api/v1/analytics/meta 访问的服务,无法正确返回 CubeMeta 信息
  • 相关测试方法绑定的是错误名,未能正确检验接口一致性
  • 下游(如 ObjectUI)的集成代码被迫实现额外适配,违背设计初衷

参考定位

  • spec/src/contracts/analytics-service.ts:合约声明的是 getMeta()
  • runtime/src/http-dispatcher.ts:实际实现调用 getMetadata(),签名不一致,还多传了 context 参数
  • runtime/src/http-dispatcher.test.ts:测试mock实现用了错误的方法名

修复建议

  1. 统一方法名:把 http-dispatcher.ts/相关测试内所有 getMetadata 替换为 getMeta,确保遵循合约名称
  2. 对齐方法签名:移除对 context 的强制传参,遵循合约签名(可后续扩展为可选参数)
  3. 同步测试:所有测试stub中的方法名同步修正为 getMeta,并保持接口行为一致
  4. (推荐)合约扩展:如有必要,建议扩展 IAnalyticsService 合约允许可选的 context 参数(用于审计/权限扩展),但需保证后兼容

变更影响

  • 修复后 downstream 项目的 analytics 适配逻辑可明显简化,无需多层适配
  • 避免未来接口漂移和约定缺失带来隐蔽bug
  • 保证 MSW、测试、生产环境行为一致,降低维护成本

请在修复后:

  • 运行全量测试
  • 更新相关下游项目的适配代码(如 ObjectUI)
  • 在 ROADMAP.md 中更新修复记录</issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 4, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
objectstack-play Ready Ready Preview, Comment Mar 4, 2026 7:13am
spec Ready Ready Preview, Comment Mar 4, 2026 7:13am

Request Review

…ticsService contract

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix analytics service method naming inconsistency fix: align HttpDispatcher analytics call with IAnalyticsService contract Mar 4, 2026
@hotlong
Copy link
Copy Markdown
Contributor

hotlong commented Mar 4, 2026

@copilot 仔细评估是否完成了任务中要求的所有开发并且不再有类似的问题

…ls to match contract

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 4, 2026

@copilot 仔细评估是否完成了任务中要求的所有开发并且不再有类似的问题

经过全面审查,发现 handleAnalytics 中还有两处类似问题未修复:

  • query(body, { request: context.request }) — 合约签名是 query(query: AnalyticsQuery),多传了 context
  • generateSql(body, { request: context.request }) — 合约签名是 generateSql?(query: AnalyticsQuery),多传了 context

已在 a42a8d5 中移除这两处多余的 { request: context.request } 参数,现在三个方法调用都与 IAnalyticsService 合约完全对齐。85 个运行时测试全部通过。

Copilot AI changed the title fix: align HttpDispatcher analytics call with IAnalyticsService contract fix: align all HttpDispatcher analytics calls with IAnalyticsService contract Mar 4, 2026
@hotlong
Copy link
Copy Markdown
Contributor

hotlong commented Mar 4, 2026

@copilot 14:34:35.528 @objectstack/spec:build: ✓ data/TenantDatabaseLifecycle.json
14:35:21.895 @objectstack/runtime:build: src/http-dispatcher.ts(529,68): error TS6133: 'context' is declared but its value is never read.
14:35:21.899 @objectstack/runtime:build: �[31mError: error occurred in dts build
14:35:21.899 @objectstack/runtime:build: at Worker. (/vercel/path0/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/dist/index.js:1545:26)
14:35:21.899 @objectstack/runtime:build: at Worker.emit (node:events:508:28)
14:35:21.899 @objectstack/runtime:build: at MessagePort. (node:internal/worker:332:53)
14:35:21.899 @objectstack/runtime:build: at [nodejs.internal.kHybridDispatch] (node:internal/event_target:845:20)
14:35:21.899 @objectstack/runtime:build: at MessagePort. (node:internal/per_context/messageport:23:28)�[39m
14:35:21.959 @objectstack/runtime:build:  ELIFECYCLE  Command failed with exit code 1.
14:35:21.973 @objectstack/runtime:build: ERROR: command finished with error: command (/vercel/path0/packages/runtime) /vercel/.local/share/pnpm/.tools/pnpm/10.28.1/bin/pnpm run build exited (1)
14:35:21.974 @objectstack/runtime#build: command (/vercel/path0/packages/runtime) /vercel/.local/share/pnpm/.tools/pnpm/10.28.1/bin/pnpm run build exited (1)
14:35:21.981 ERROR run failed: command exited (1)
14:35:22.000 Error: Command "cd ../.. && pnpm turbo run build --filter=@objectstack/studio" exited with 1

…error

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 4, 2026

@copilot 14:34:35.528 @objectstack/spec:build: ✓ data/TenantDatabaseLifecycle.json
14:35:21.895 @objectstack/runtime:build: src/http-dispatcher.ts(529,68): error TS6133: 'context' is declared but ...

Fixed in 3d0775e — after removing the { request: context.request } parameters, context in handleAnalytics became unused, triggering TS6133. Prefixed it as _context following the existing convention used by handleI18n and handleUi. Build and all 85 tests pass.

@hotlong hotlong marked this pull request as ready for review March 4, 2026 07:24
Copilot AI review requested due to automatic review settings March 4, 2026 07:24
@hotlong hotlong merged commit 5d862a2 into main Mar 4, 2026
4 checks passed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a method-naming misalignment between HttpDispatcher's analytics handling and the IAnalyticsService contract defined in packages/spec. The dispatcher was calling getMetadata() (non-existent) and passing extra { request: context.request } arguments to query() and generateSql(), neither of which matches the contract interface. The PR renames the call to getMeta(), removes the extra arguments, and updates tests and the ROADMAP accordingly.

Changes:

  • handleAnalytics in http-dispatcher.ts: rename getMetadatagetMeta, remove non-contract { request: context.request } arguments from all three analytics service calls, and prefix unused context param as _context.
  • http-dispatcher.test.ts: rename two mock method definitions from getMetadata to getMeta.
  • ROADMAP.md: add a fix record entry documenting the naming correction.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
packages/runtime/src/http-dispatcher.ts Fixes getMetadatagetMeta, removes extra context args, renames unused param to _context
packages/runtime/src/http-dispatcher.test.ts Updates two mock definitions from getMetadata to getMeta to match corrected dispatcher
ROADMAP.md Documents the analytics naming fix in the project roadmap

const mockAnalytics = {
query: vi.fn().mockResolvedValue({ rows: [{ id: 1 }], total: 1 }),
getMetadata: vi.fn().mockResolvedValue({ tables: ['t1'] }),
getMeta: vi.fn().mockResolvedValue({ tables: ['t1'] }),
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as line 248: the mock at line 219 returns { tables: ['t1'] } (an object), while the IAnalyticsService.getMeta() contract specifies Promise<CubeMeta[]> (an array). The mock should use the correct contract-conformant return type: an array of CubeMeta objects.

Copilot uses AI. Check for mistakes.
if (subPath === 'sql' && m === 'POST') {
// Assuming service has generateSql method
const result = await analyticsService.generateSql(body, { request: context.request });
const result = await analyticsService.generateSql(body);
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generateSql method is declared optional in IAnalyticsService (generateSql?(query: AnalyticsQuery): ...), but it is called unconditionally here without a null/undefined guard. If an analytics service only implements the required query and getMeta methods (which is a valid implementation per the contract), a POST /analytics/sql request will throw TypeError: analyticsService.generateSql is not a function at runtime.

The fix should check for the method's existence before calling it, for example: if the method doesn't exist, return a 501 Not Implemented response or { handled: false }.

Copilot uses AI. Check for mistakes.
it('should handle GET /analytics/meta with async service', async () => {
const mockAnalytics = {
getMetadata: vi.fn().mockResolvedValue({ tables: ['users', 'orders'] }),
getMeta: vi.fn().mockResolvedValue({ tables: ['users', 'orders'] }),
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mock at line 248 returns { tables: ['users', 'orders'] } (a plain object with a tables key), but the IAnalyticsService.getMeta() contract specifies the return type as Promise<CubeMeta[]> — an array of CubeMeta objects. The same non-conformant mock shape also appears at line 219.

This means the test assertion at line 255 (result.response?.body?.data?.tables) is validating an artificial response shape rather than the actual contract. The mock should return a proper CubeMeta[] array (e.g., [{ name: 'users', measures: [], dimensions: [] }, { name: 'orders', measures: [], dimensions: [] }]) and the assertion should validate the returned array directly.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Analytics服务接口方法命名不一致,导致端点功能失效

3 participants