Repository navigation
fix(service-analytics)!: the dataset draft preview refuses a runtimeFilter on a field the object does not have, and a dimension over a JSON column, as the live strategies do - #28264
Conversation
…ilter on a field the object does not have, and a dimension over a JSON column, as the live strategies do The draft preview evaluated a runtimeFilter over the seed rows without asking whether the object has each filtered field, so a misspelled filter served [] (or, under an $or, the other arm's rows) where both live strategies refuse it INVALID_FIELD / 400 with param 'where'. It also grouped by a json column, one group per object value, where live refuses it with param 'dimensions'. The preview now asks the structured-JSON dimension door after the dimension-field gate and the where-field gate last among ensureCube's gates, before query()'s caller-measure gate: the live order. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cyQNVxpxREGfKkTsqs
|
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia Contract-review-claim: The lane seat runs the |
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 10 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 1c77b3e8c08327c552d39166449f9fbb3a7ab297 && git checkout 1c77b3e8c08327c552d39166449f9fbb3a7ab297
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin be646edeb1975a6b510d742f04185475d315f9b4 83c8631e766f79574c0d20fb2a51e7759b934916 && git checkout -B drift-repro be646edeb1975a6b510d742f04185475d315f9b4 && git merge --no-ff 83c8631e766f79574c0d20fb2a51e7759b934916
node scripts/docs-audit/affected-docs.mjs --json be646edeb1975a6b510d742f04185475d315f9b4 |
|
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia Contract reviewServed-tier: ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Implemented-by: VERDICT: PASS |
|
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia Seat ACCEPT · PR #28264 · head
|
Requested by Elon · project thread
domain:server#3 · os-elon-musk · session_016WV4cyQNVxpxREGfKkTsqs
Fixes #28257
Clause-②: no (narrowing) — a draft-preview dataset query (
queryDatasetwithpreviewDrafts: trueover a pending seed draft) now refuses aruntimeFiltermember whose field the object does not have, and a grouped dimension over ajsoncolumn (#20807), both of which the native and ObjectQL strategies refuse and the preview served before. No export, answer member, status or error code is added: both refusals are the live faces' existingINVALID_FIELD/ 400.Metadata-protocol: none
维护者速读
runtimeFilter: { bogus: 'x' }),过去返回[],放在$or里还会返回另一分支的行;现在与实时两种策略一样按 400INVALID_FIELD(param: where)拒,消息逐字相同。triage 并入的 [finding] analytics: a cube / dataset dimension on ajsonfield, compiled by NativeSQLStrategy, answers one group per serialized document on SQLite and 500 on PostgreSQL; the engine door #20783 closes does not see it #20807:按json字段分组,预览过去按对象值分组,实时拒;实测确有差异,同一 PR 一并对齐。$in)三面照旧返回同样的行,实时策略不变。回滚即 revert。packages/services/service-analytics/src/analytics-service.ts里answerDataset的previewService.query(新增两道闸门及其位置)。pnpm --filter @objectstack/service-analytics exec vitest run src/__tests__/preview-filter-field-json-dimension-parity.test.ts,预期 11 passed。What changes
The draft preview's gate list (
analytics-service.ts,answerDataset→previewService.query). Two gatesensureCubeasks on the live path are now asked on the preview, on the cube with the minted measures, in the order #28259 set up:assertDimensionsGroupScalarColumns([finding] analytics: a cube / dataset dimension on ajsonfield, compiled by NativeSQLStrategy, answers one group per serialized document on SQLite and 500 on PostgreSQL; the engine door #20783 closes does not see it #20807), new hereassertWhereFields(analytics:where里点名不存在的字段仍然一路到驱动 —— #4437(measure)/ #5520(dimension)之后,filter 面是同一个缺陷剩下的第三个 param #5669), new here, last ofensureCube'squery()'s: caller-measure column reference ([security] An analytics query path can carry caller-supplied member text into the native statement in a tier the field gate does not judge — detail withheld pending maintainer #21156), stored body ([security] Family closeout: stored metadata bodies (stored datasource credential material included) are still reachable outside the redacting doors at several further positions — detail withheld pending maintainer #21120), internal fields, field-level read (security(analytics): the native-SQL strategy answers a query naming a field the caller has no field-level read permission for, where the engine and the ObjectQL strategy refuse 403 #20917).The where gate takes the compiled cube's declared dimensions as its vocabulary, as live does, so a filter on a declared dimension or on any of the object's own fields stays legal. It reads every leaf of the normalized filter tree, so the object spelling, an
$orarm and the array triple are refused alike. The JSON door refuses ajsoncolumn whether the dataset declares the dimension over it or the caller names the field.preview-evaluator.tsis unchanged: the gates run before any seed row is read.Why
Measured on
main4159c701, real SQLite engine, object fieldsregion(text),amount(number),payload(json); dataset dimensionsregion,payload_dim(overpayload), measurestotal(sum ofamount),deals(count):runtimeFilter: { bogus: 'x' }INVALID_FIELD/ 400,param: where[]runtimeFilter: { $or: [{ bogus: 1 }, { region: 'amer' }] }INVALID_FIELD/ 400runtimeFilter: [['bogus', '=', 'x']]INVALID_FIELD/ 400[]region,amount: { $gt: 40 },region: { $in }['payload'](json, undeclared)INVALID_FIELD/ 400,param: dimensions['payload_dim'](json, declared)INVALID_FIELD/ 400,param: dimensions['payload']+ filter{ ghost: 1 }[]amount + 1+ filter{ ghost: 1 }Verification
packages/services/service-analytics/src/__tests__/preview-filter-field-json-dimension-parity.test.ts(new, 11 cases, each over native / ObjectQL / preview throughqueryDataseton real SQLite): the unknown filter field refusedINVALID_FIELD/ 400 withparam: 'where',fieldand a message identical on all three faces, in three spellings; CONTROLS: three real-field filters serve the same rows on all three; the json dimension refused withparam: 'dimensions'and one message, declared and undeclared; three two-mistake cases answer the same refusal first on all three (json dimension + unknown filter → dimension; json dimension +region_sum→ the JSON door ahead of the field-type door;amount + 1+ unknown filter → where 400 ahead of the 403).analytics-service.tsreset tomain⇒ 8 red (all but the three controls);assertWhereFieldsremoved ⇒ 4 red (three spellings,amount + 1+ filter);assertDimensionsGroupScalarColumnsremoved ⇒ 4 red (both json dimensions, both json two-mistake cases); the where gate moved after the caller-measure gate ⇒ 1 red (amount + 1+ filter); the JSON door moved after the currency door ⇒ 1 red (json dimension +region_sum).pnpm --filter @objectstack/service-analytics exec vitest run --maxWorkers=2at head: 200 files, 4806 passed, 282 skipped, 0 failed (the fix(service-analytics)!: the dataset draft preview mints and judges an undeclared measure as the live strategies do, so a misspelled measure is refused INVALID_FIELD instead of answered as a row count #28210 and fix(service-analytics)!: the dataset draft preview refuses a dimension whose field the object does not have, as the live strategies do, instead of grouping every row under one null key #28259 parity files included).pnpm --filter @objectstack/service-analytics typecheckand eslint on the touched files clean.dispatch-gates --commandsfamily,pnpm lintand the dogfood battery run at this head: the readings are posted on the card with the os-dev report.Skills-docs: none.
git grep -n -i -E "previewDrafts|draft (data )?preview" -- skills content/docs(references and releases excluded) finds onlycontent/docs/data-modeling/currency.mdx:146, still true;git grep -n runtimeFilterfindscontent/docs/data-modeling/analytics.mdxandcontent/docs/ui/{dashboards,reports}.mdx, which describe the filter vocabulary and say the Studio preview shares the governed path ("so the numbers match everywhere",analytics.mdx:263), which this PR makes true for filters; nothing describes the preview serving an unknown filter field.Human Minimum
packages/specholds no runtime logic.pnpm lintpasses. (No formatter ran; the lint reading is posted on the card with the gate battery.)main, one pull request.packages/spectouched: not touched.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_016WV4cyQNVxpxREGfKkTsqs