Repository navigation
fix(service-datasource): Setup → Datasources: Test answers truthfully for the primary datasource and for drivers that cannot introspect; Sync on the primary datasource refuses with the true reason - #24852
Conversation
…river; Sync refuses it with the true reason The Datasources page read the primary datasource as connected (true: its driver is live under its natural name, ADR-0062), while Test answered ok:false and Sync 400, both saying it had no registered driver. - registeredDriverName(): the default-driver resolver disconnect() applied, extracted so the Test probe reuses it instead of restating it. - testConnection(): with a liveDriver wired, the primary datasource and a driver without introspectSchema (memory, mongodb) answer checkHealth(); the latter says introspection: 'unsupported'. Every other datasource is tested as before. - listRemoteTables / generateObjectDraft / importObject / refreshCatalog refuse the primary datasource with the same 400 and code as before, and a true reason: it is the platform's managed storage, not a federation source (ADR-0015 section 1). Claude-Session: https://claude.ai/code/session_01UmqzD97e14iws6hmVhgZ5y Co-authored-by: Claude <noreply@anthropic.com>
… and on a driver that cannot introspect Through the real composition: a real ObjectQL engine, the connection service connecting the primary datasource asDefault, the federation plugin's wiring and the admin routes over Hono. Plus the patch changeset. Claude-Session: https://claude.ai/code/session_01UmqzD97e14iws6hmVhgZ5y Co-authored-by: Claude <noreply@anthropic.com>
…fault-datasource-status
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 2 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 e4c8774b485a70e37a4df8a6cd8ae9506c61ffed && git checkout e4c8774b485a70e37a4df8a6cd8ae9506c61ffed
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 4be72813112e0f865f1890ba6540ae0a496a5664 3226d40195bb25ed73a2bd81a428252b59c4159e && git checkout -B drift-repro 4be72813112e0f865f1890ba6540ae0a496a5664 && git merge --no-ff 3226d40195bb25ed73a2bd81a428252b59c4159e
node scripts/docs-audit/affected-docs.mjs --json 4be72813112e0f865f1890ba6540ae0a496a5664
|
席位核对REWORK (patch round 1) · #24772 · The route matches the ruling: the
The dev's out-of-scope note (the draft probe discards Generated by Claude Code |
…hangeset declares the liveDriver widening
A driver without introspectSchema answers its health probe as
{ ok, latencyMs } with no tableCount, like the primary datasource: the
`introspection` key is dropped, since nothing reads it. The changeset is a
minor bump with Clause-② yes, naming the one published-surface addition,
the optional ExternalDatasourceServiceConfig.liveDriver callback.
Claude-Session: https://claude.ai/code/session_01UmqzD97e14iws6hmVhgZ5y
Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: The face is the ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
席位核对ACCEPT · #24772 ·
Generated by Claude Code |
Fixes #24772
Clause-②: yes (widening)
What was wrong
Setup → Datasources listed the primary datasource as
default · sqlite · connected, and that is true.DefaultDatasourcePluginconnects it throughDatasourceConnectionServicewithasDefault, and ADR-0062 registers its driver under the driver's own name (com.objectstack.driver.sql), not underdefault. Test and Sync then introspected by the namedefault. The engine's registry has no entry by that name, so both answered[ObjectQL] Datasource 'default' has no registered driver to introspect.: Test as200 { ok: false }, Sync as400 EXTERNAL_DATASOURCE_ERROR. A connectedmemorydatasource got the sameok: falsefrom Test, because Test was an introspection round trip and that driver has nointrospectSchema.Measured before the change, on
origin/mainc3a8e07b6, through the real admin routes over a realObjectQLengine, the real connection service and the realExternalDatasourceServicePlugin:What changed (route D, as the seat ruled, comment 6287770114)
All of it is in
packages/services/service-datasource/src. Nothing inpackages/objectql.registeredDriverName()indatasource-connection-service.tsis the default-driver resolverDatasourceConnectionService.disconnect()applied inline. It is extracted, anddisconnect()calls it with its ownasDefaultflag, so its behaviour is unchanged. The Test path calls the same function.disconnectAll()names the reserved datasource through the same constant.POST /api/v1/datasources/:name/test. The plugin wires a new optionalliveDriverseam: the engine's registry entry under the nameregisteredDriverName()resolves.testConnection()then:checkHealth(), giving{ ok: true, latencyMs }, and does not introspect the platform database;introspectSchema(memory,mongodb), answerscheckHealth()with the answer's existing keys,{ ok: true, latencyMs }and notableCount, instead ofok: false;ok: falsewith a reason;tableCountand the same engine message.listRemoteTables,generateObjectDraft,importObjectandrefreshCatalogrefusedefaultbefore any introspection. The status and code are the ones each door served before:400 EXTERNAL_DATASOURCE_ERROR, orEXTERNAL_IMPORT_ERRORon the import. The message is now the true reason:Datasource 'default' is the platform's managed primary datasource, not a federation source: its tables are the platform's own objects, so there are no remote tables to list, draft or import from it. Sync objects from an external datasource instead.The measured choice for Test on
default:checkHealth(), not introspection by the resolved nameBoth reach the live driver.
checkHealth()was chosen for three reasons:SqlDriveron SQLite in memory:introspectSchema()took 14.0 ms at 10 tables and 132.0 ms at 120 tables.checkHealth()took 0.79 ms and 0.28 ms. On a networked Postgres, each of those per-table round trips also pays the network latency.checkHealth()is that question (SELECT 1on SQL,pingon MongoDB).tableCountof the platform's tables answers nothing the page uses. No consumer in either repository readstableCount.Every other datasource keeps the introspection probe and its
tableCount, so nothing moves for a federation source.The seat's premises, re-checked before editing
disconnect()applied (opts.asDefault ? engine.getDefaultDriverName() : name) needs onlygetDefaultDriverNameandgetDriverByName. Both are on the'data'engine's published contract (IDataEngine,packages/spec/src/contracts/data-engine.ts), which the plugin already reads. No objectql change is needed.driver-catalog.ts) listsmemory,sqlite,postgres,mysqlandmongodb. The two withoutintrospectSchemahavecheckHealth():MemoryDriver.checkHealth()returnstrue, andMongoDBDriver.checkHealth()runsdb.command({ ping: 1 }).IDataDriverdeclarescheckHealth()as required.Evidence after the change
packages/services/service-datasource/src/__tests__/primary-datasource-test-and-sync.test.tshas 10 cases. They run through the real composition: a realObjectQLengine (its source, through this package's vitest alias), the connection service connectingdefaultwithasDefaultexactly asDefaultDatasourcePlugin.init()does, the plugin's real wiring, and the admin routes over Hono.default, the list readsok, and Test answers200 { ok: true, latencyMs }with noerrorand notableCount. The driver'sintrospectSchemais never called.default,remote-tablesandobject-draftanswer400, witherror.codeEXTERNAL_DATASOURCE_ERRORand the true message. The import refuses with{ code: 'EXTERNAL_IMPORT_ERROR', status: 400 }, and the list and catalog refresh with{ code: 'EXTERNAL_DATASOURCE_ERROR', status: 400 }. The platform database is never introspected.memorydatasource tests{ ok: true, latencyMs }, exactly those two keys. A registered driver whose health check answersfalseor throws givesok: false.{ ok: true, tableCount: 1 }and still lists its tables with200. That is the seat's ban on changing Sync for other managed datasources. Sync onmemorystill refuses with the engine's reason. A datasource with no live driver still answersok: false.Ablations, one behaviour each. Each was run through
scripts/ablation-replace.mjs: the anchor hit once, the on-disk blob changed, and the restore was proven blob == HEAD with an emptygit diff HEAD. The subject is this package's own source, imported relatively, so no build sits between the mutation and the reading.registeredDriverNameexpected { ok: false, …(1) } to match object { ok: true }listRemoteTables, and introspection resolves the defaultexpected 200 to be 400, andlistRemoteTables('default')resolved a valueintrospectSchemagoes back to the introspection round tripexpected { ok: false, …(1) } to match object { ok: true }(re-run at3226d4019)defaultintrospects by the resolved nameexpected [ 'latencyMs', 'ok', 'tableCount' ] to deeply equal [ 'latencyMs', 'ok' ](re-run at3226d4019)expected true to be falseintrospectionkey put back on a driver that cannot introspectexpected [ 'introspection', 'latencyMs', 'ok' ] to deeply equal [ 'latencyMs', 'ok' ](at3226d4019)The published-surface addition: Clause-② yes (widening)
ExternalDatasourceServiceConfigis exported from the package's.entry, so its new optional memberliveDriverwidens the published surface. The changeset is aminorbump that declaresClause-②: yes (widening)and names that member. No status code, error code or accepted value moves on any route. Test's answer keeps its existing keys (ok,latencyMs,tableCount,error): theintrospectionkey from the first head was dropped, because nothing reads it.ExternalDatasourceServiceConfig.liveDriver?: a host that builds the service without it keeps the old probe.registeredDriverNameandDEFAULT_DATASOURCE_NAMEare module exports only, not exported from the package entry.Acceptance notes
refreshCatalogis included under the bounded in-place exemption. It is the same defect class (the same false "no registered driver" answer fordefault), in the same file, through the same refusal helper. Its door (POST …/external/refresh-catalog) keeps400 EXTERNAL_DATASOURCE_ERROR.default(ADR-0062). The only way to register a driver literally nameddefaultisObjectQL.create({ datasources: { default: driverWithNoName } }). A repository grep finds no production caller ofObjectQL.create, and every in-tree driver carries its own name. So no in-tree composition answered200on Sync fordefaultbefore this change.38-integrations-datasources-created.pngshows Edit and Delete on the runtimeqa_memrow. Only the code-origindefaultrow lacks them: code-defined datasources are read-only (datasource-admin-service.tsheader), and objectui hides both onorigin: 'code'.ObjectQL.introspectDatasourcestill does not apply ADR-0062 (no objectql edit). Sync on other managed runtime datasources is unchanged (route B, filed separately by the seat). The two objectui renderer findings are filed by the seat.checkHealth().DatasourceAdminServicePlugin.probe, behindPOST /api/v1/datasources/test, awaitsdriver.checkHealth()and ignores the boolean it returns, so a draft whose driver's health check answersfalsewould reportok: true. This comes from reading the code only: no public-door reading was taken.Verification
At HEAD
3226d4019(no further merge oforigin/main: no conflict and no main-caused red, AGENTS.md §10):pnpm --filter '@objectstack/service-datasource^...' build: exit 0.pnpm --filter @objectstack/service-datasource build: exit 0, andcheck-dts-emittedreports 4/4.pnpm --filter @objectstack/service-datasource typecheck: exit 0.--listFilesincludes the new test file.pnpm --filter @objectstack/service-datasource test: 48 files, 862 tests passed, 47.6 s.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(69 commands): 68 exited 0 and 1 was not measured.pnpm check:dual-build-cjs-loadsexited 3 (PREREQUISITE NOT MET: it reads every package's built output, and this worktree builds only this package's closure), so CI'sLint & Repo Gatesis its reading.--ranreconciliation:69 derived, 68 run, 1 NOT-MEASURED, 0 UNRUN, with an exit code on every line.node scripts/check-platform-checklist.mjsis not owed: no exported symbol is renamed or deleted.test:repowas not run locally. CI owns it, and this diff touches no test of that project.Generated by Claude Code