Repository navigation
feat(driver-odata): an OData v2 dialect beside v4, selected with version: '2.0' - #28177
Conversation
…ion: '2.0' The v2 dialect plugs into the existing dialect seam: the verbose JSON envelope (d.results, __count, __next, __metadata), $inlinecount, the typed v2 literals and key predicates, substringof, MERGE tunnelled through POST, and EDMX 1.0 $metadata whose Associations and AssociationSets are read into the navigation shape v4 yields, so an import drafts the same lookups. Edm.DateTime, Edm.Int64, Edm.Decimal and Edm.Time values are read and written in their v2 JSON forms; a date field over an Edm.DateTime reads its date. The spec type matrix's odata arm maps the two v2-only primitives, Edm.DateTime and Edm.Time. Vendor mechanics (SAP's CSRF handshake, sap-client) stay out. The conformance kit and the eleven shared case-sets now also run over an in-process v2 service that refuses every v4 spelling. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cykf9LVyVYxnw1NL95
📓 Docs Drift CheckThis PR changes 2 package(s): 7 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 4 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 168 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 39262b3e630e444c8259fd37d92f13ed1dd442d7 && git checkout 39262b3e630e444c8259fd37d92f13ed1dd442d7
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 412908cf9e982d86307504fa6e860111c046926a 667660add518c9a9e904cf24836ea68dfb09b5a3 && git checkout -B drift-repro 412908cf9e982d86307504fa6e860111c046926a && git merge --no-ff 667660add518c9a9e904cf24836ea68dfb09b5a3
node scripts/docs-audit/affected-docs.mjs --json 412908cf9e982d86307504fa6e860111c046926a
|
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL Contract-review-claim: session_016WV4d1Np3hU7RQnA13YZcL · head 667660a · 2026-10-30T15:33Z |
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL Contract reviewServed-tier: Inputs read: card #28125 (body and all three comments: triage 6524734159, claim 6524866087, os-dev-report 6526649009), PR #28177 (body, file list, net diff against ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
No must-fix. No should-fix on the Implemented-by: VERDICT: PASS |
|
domain:server#4 · os-elon-musk · session_016WV4cykf9LVyVYxnw1NL95 PR-body addendum (the relay cannot re-edit the body). This answers the contract review's should-fix in comment 6526958904, the third declaration line that Skills-docs: updated here — Generated by Claude Code |
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL 维护者速读
席位意见:ACCEPT。 席位核对Head-sha:
|
Requested by Elon · project thread
domain:server#4 · os-elon-musk · session_016WV4cykf9LVyVYxnw1NL95
Fixes #28125
Clause-②: yes (widening)
Metadata-protocol: added
维护者速读
@objectstack/driver-odata在 v4 留好的方言接缝上新增 OData v2 方言,插件配置version: '2.0'即可接 v2 服务(默认仍是4.0)。覆盖卡上每一项:d.results/__count/__next响应封装、$inlinecount、substringof与 v2 带类型字面量(datetime'…'、guid'…'、7L、1.5M等)、键谓词、MERGE写入(POST+X-HTTP-Method)、EDMX 1.0 的 Association / AssociationSet。spec 的类型矩阵补Edm.DateTime → datetime、Edm.Time → time。version: '2.0'直接拒绝启动。SAP 专有机制(CSRF 握手、sap-client、sap:*注解)按 10-29 裁定没有放进来。unknown)。回滚即 revert。packages/drivers/driver-odata/src/dialect-v2.ts,以及odata-driver.ts里 4 个可选钩子的接入点。pnpm --filter @objectstack/driver-odata exec vitest run,预期 1383 passed(其中 v2 651 条:一致性用例集 336、插件套件 276、录制驱动 15、方言 24)。packages/spec/src/**),由席位安排。What changes
packages/drivers/driver-odata/src/dialect-v2.ts(new):odataV2DialectandparseCsdlV2, implementing the existingODataDialect.DataServiceVersion: 2.0/MaxDataServiceVersion: 2.0andAccept: application/json(verbose JSON).d.results(or a 1.0 service's baredarray), with__nextas the next link and the string__countas the count. An entity is read out ofd, and its ETag comes from__metadata.etag.__metadataand each navigation's{ __deferred }stub are dropped from the row.$inlinecount=allpages, andcontainsissubstringof('x',P).datetime'…'(only for a comparand that names its instant, spelled as its UTC wall clock),datetimeoffset'…',time'PT…',guid'…',7L,1.5M(no exponent),1.5d,1.5f.keyValuereads a composite key's typed literal back, soTrackingId=guid'…',Leg=2Lround-trips./Date(ms)/→ the UTC instant, or the date under a field declareddate(v2 has noEdm.Date).Edm.Int64/Edm.Decimaltext → number, except anEdm.Int64beyond the safe range, which stays text.Edm.TimePT13H20M→13:20:00. Writes use the inverse forms.POSTwithX-HTTP-Method: MERGE.$metadata: a navigation's target and cardinality come from itsAssociationends. ItsReferentialConstraintpairs are taken only on the dependent end, as v4 writes them on that navigation. Its entity-set binding comes from theAssociationSets, base types included.sap-clientorsap:*).dialect.ts: the seam gains four optional members (readValue,writeValue,keyValue,updateRequest). If a dialect leaves one out, the driver keeps v4's behaviour.odataV4Dialectimplements none of them.odata-driver.ts:DIALECTS['2.0']is the v2 dialect.toRowreads each declared property throughreadValue, andtoRemotewrites it throughwriteValue.updatetakes its verb fromupdateRequest, and the composite key predicate is re-spelled throughkeyValue.registerExternalObjectrecords each field's declared type from the object'sfields, the way the SQL driver learns field types, soreadValuesees it.config.ts:version: z.enum(['4.0', '2.0']), default4.0.dialect-v4.ts: pure refactor: the XML helpers are exported, and the inheritance flattening is extracted intoflattenEntityTypesfor the v2 reader. The v1/EDMX refusal message now namesversion: '2.0'and still matches the v4 suite's pin.packages/spec/src/data/type-compat.ts:DIALECT_ALIASES.odatagainsedm.datetime→datetimeandedm.time→time, and nothing else.northwind-v2-metadata.xmlis Northwind's v2 shape, abridged, plus two fixture-own sets with a Guid key and a composite (Guid, Int64) key.recorded-service-v2.tsholds the verbose JSON recordings. Both are written from the service's shape, because the container has no network.dialect-v2.test.tshas 24 tests andodata-driver-v2.test.tshas 15.odata-service.ts) gains a strictversion: '2.0'mode: EDMX 1.0, thedenvelope,$inlinecountandMERGE. It refuses v4's$count,contains, bare temporal literals andPATCH.odata-driver.conformance.test.ts) and the plugin-driver kit (…conformance-kit.test.ts) now also run over that v2 service. The v4 describe names are unchanged; the kit's object is registered with itsfields, as the engine registers it.service-datasource'sexternal-object-draft-odata-metadata.test.tsgains a v2 describe over its own copy of the v2$metadata. This test-only path was added to the claim's surface by the seat.content/docs/data-modeling/odata-datasources.mdxgains an "OData v2 services" section with theversion: '2.0'line, a v4/v2 spelling table, and what stays out.README.mdand the package description name v2..changeset/28125-driver-odata-v2-dialect.md:@objectstack/driver-odataminor and@objectstack/specminor (added).Why
Before (measured at
origin/main062a2323, production sources reverted, new tests kept)driver-odata: invalid config — version: Invalid input: expected "4.0".dialect-v2.test.tscould not load, because the module did not exist. The 732 passing tests are the whole v4 suite.type-compat.test.ts: 1 failed,expected 'unknown' to be 'datetime'.After
All green: driver-odata 1383 passed, the service-datasource import 18 passed, and spec
type-compat29 passed.Clause-② and the kind
yes (widening): the driver config accepts a secondversionvalue, and the type matrix answersdatetime/timewhere it answeredunknown(a draft typed those columnstextfor review, and Gate 2 refused adatetime/timefield over one). Nothing accepted before is refused now.Metadata-protocol: added:version: '2.0'is a new value a datasource author can write, so both moved packages takeminor.One finding met on the way
The plugin-driver kit's "temporal: every row, as stored" check failed on the first v2 run. A
datefield over v2'sEdm.DateTimeread back as1969-12-31T00:00:00.000Zwhile the remote held1969-12-31. v2 has noEdm.Date, and a v2 service keeps a date in anEdm.DateTimeat midnight. So the driver now reads the date alone under a field the object declaresdate, and the kit registers its object with itsfields, as the engine does.Verification
Pins red first, ablated, restored. Each ablation used
scripts/ablation-replace.mjson the committed WIP head, each restored withgit diff HEADempty. Every one went red:d.results)ReferentialConstraintsubstringofdatetime'…'prefixguid'…'key literal$inlinecountversionaccepting2.02.0selecting the v2 dialectMERGEEdm.DateTime__deferredstubsOne extra run stayed green: the spec-alias ablation against the service-datasource import test. service-datasource resolves
@objectstack/specfrom its builtdist, so asrcmutation does not reach it. The alias pin is the spec test, which went red.The v4 suite is unchanged. Its 732 tests passed on
main's sources and pass on this head. The shared test files gained a version loop, and the v4 run keeps its exact describe names.Gates. On the committed head
667660ad, after a whole-treeturbo run build(exit 0):dispatch-gates --commandsderived 117 families, all run, all exit 0.--ran: 117 derived, 117 run, 0 NOT-MEASURED. Also run:pnpm lintexit 0;typecheckof driver-odata, service-datasource and spec, each exit 0; driver-odata 1383 passed; service-datasource 1074 passed; specsrc/data4591 passed. The head is 15 commits behindorigin/main; none of them touches a file this diff changes.Generated spec artifacts.
pnpm --filter @objectstack/spec buildthencheck:generated: all 14 up to date (the alias table feeds no artifact).Acceptance notes
Prefer: odata.maxpagesizeis still sent on a v2 GET. A v2 service ignores a preference it does not know (RFC 7240 §2), and the page size it uses is its own. The docs and thepageSizedescribe say so. The transport is untouched.Edm.DateTimeis not pushed down. It is left to the engine, the same rule v4 applies toEdm.DateTimeOffset, so the service is never asked a narrower question.Human Minimum
packages/specholds no runtime logic. (Two alias entries in a pure mapping table.)pnpm lintpasses.main, one pull request.packages/spectouched: its generated artifacts are checked (check:generated: all up to date, nothing to regenerate).content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_016WV4cykf9LVyVYxnw1NL95