Skip to content

Commit 3226d40

Browse files
committed
fix(service-datasource): Test keeps its answer's existing keys; the changeset 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>
1 parent 07aba47 commit 3226d40

3 files changed

Lines changed: 28 additions & 29 deletions

File tree

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
---
2-
'@objectstack/service-datasource': patch
2+
'@objectstack/service-datasource': minor
33
---
44

55
fix(service-datasource): Setup → Datasources: Test answers truthfully for the platform's primary datasource and for drivers that cannot introspect, and Sync on the primary datasource refuses with the true reason
66

7-
Clause-②: no (no status code, error code or accepted value moves on any route; Test's answer gains one optional key, `introspection: 'unsupported'`, and `ExternalDatasourceServiceConfig` one optional member, `liveDriver`)
7+
Clause-②: yes (widening: the published `ExternalDatasourceServiceConfig`, exported from the package entry, gains one optional member, the `liveDriver` callback; no status code, error code or accepted value moves on any route, and Test's answer keeps its existing keys)
88

99
- **What was wrong.** The Datasources page listed the primary datasource (`default`) as connected, which is true. Its Test then answered `200 { ok: false }` and Sync answered `400`, both saying the datasource "has no registered driver to introspect". The primary datasource's driver is registered under the driver's own name, not under `default`, and both doors looked it up by `default`. A connected `memory` datasource also got `ok: false` from Test, because Test was an introspection round trip and that driver cannot introspect.
10-
- **Test, `POST /api/v1/datasources/:name/test`.** For the primary datasource, Test now finds the live driver the way the connection service's own teardown does and answers its health check: `{ ok: true, latencyMs }`. It does not list the platform database's tables. For a driver with no schema introspection (`memory`, `mongodb`), Test answers the health check too and says `introspection: 'unsupported'`, instead of `ok: false`. A driver that fails its health check answers `ok: false`. Every other datasource is tested exactly as before (an introspection round trip with `tableCount`), and so is a datasource with no live driver.
10+
- **Test, `POST /api/v1/datasources/:name/test`.** For the primary datasource, Test now finds the live driver the way the connection service's own teardown does and answers its health check: `{ ok: true, latencyMs }`. It does not list the platform database's tables. For a driver with no schema introspection (`memory`, `mongodb`), Test answers the health check too, `{ ok: true, latencyMs }` with no `tableCount`, instead of `ok: false`. A driver that fails its health check answers `ok: false`. Every other datasource is tested exactly as before (an introspection round trip with `tableCount`), and so is a datasource with no live driver.
1111
- **Sync on the primary datasource.** `GET …/remote-tables`, `POST …/object-draft`, the import (`POST …/external/tables/:remote/import`) and the catalog refresh still refuse `default`, with the same status and code as before (`400 EXTERNAL_DATASOURCE_ERROR`, or `EXTERNAL_IMPORT_ERROR` on the import). The message now gives the true reason: the primary datasource is the platform's managed storage, not a federation source, so there are no remote tables to list, draft or import from it. Its tables are never introspected.
1212
- **Unchanged.** Sync on every other datasource, including a managed runtime one, answers exactly as before.
13+
- **New, optional: `ExternalDatasourceServiceConfig.liveDriver`.** `(datasource) => driver | undefined` returns the live driver registered for a datasource, resolved by the primary datasource's naming rule. `ExternalDatasourceServicePlugin` wires it. Test reads it to reach the primary datasource and drivers without schema introspection.
1314

14-
What to do: nothing. If you build `ExternalDatasourceService` yourself, wire `liveDriver` to get the new Test answers; without it, Test stays the introspection round trip.
15+
What to do: nothing, if the plugin builds the service. If you build `ExternalDatasourceService` yourself, pass `liveDriver` to get the new Test answers; without it, Test stays the introspection round trip, as before.

‎packages/services/service-datasource/src/__tests__/primary-datasource-test-and-sync.test.ts‎

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
*
1717
* - Test answers truthfully for both. The primary datasource's driver is
1818
* found by the resolver `DatasourceConnectionService.disconnect` already
19-
* applies (`registeredDriverName`), and a driver that cannot introspect says
20-
* so instead of failing.
19+
* applies (`registeredDriverName`), and a driver that cannot introspect
20+
* answers its health probe instead of failing. Both answer the existing
21+
* `{ ok, latencyMs }` keys and nothing more (the seat's REWORK record:
22+
* no answer key without a consumer).
2123
* - Sync on `default` keeps refusing with the SAME status and code, and now
2224
* gives the true reason: the primary datasource is the platform's managed
2325
* storage, not a federation source (ADR-0015 §1). It never introspects the
@@ -162,10 +164,11 @@ describe('the primary datasource: Test agrees with the "connected" the page list
162164
expect(test.status).toBe(200);
163165
expect(test.body.data).toMatchObject({ ok: true });
164166
expect(typeof test.body.data.latencyMs).toBe('number');
165-
expect(test.body.data).not.toHaveProperty('error');
166167
// The primary datasource is not a federation source: Test asks the driver
167168
// whether it is connected, never lists the platform's tables to find out.
168-
expect(test.body.data).not.toHaveProperty('tableCount');
169+
// So the answer is the existing keys `ok` and `latencyMs`, with no
170+
// `tableCount` and no `error`.
171+
expect(Object.keys(test.body.data).sort()).toEqual(['latencyMs', 'ok']);
169172
expect(introspect).not.toHaveBeenCalled();
170173
});
171174
});
@@ -202,13 +205,16 @@ describe('the primary datasource: Sync refuses as before, with the true reason',
202205
});
203206

204207
describe('a driver with no introspectSchema', () => {
205-
it('Test answers connected and says introspection is unsupported, instead of ok: false', async () => {
208+
it('Test answers its health probe, connected, instead of ok: false', async () => {
206209
const { call } = await boot();
207210

208211
const test = await call('POST', '/scratch/test');
209212
expect(test.status).toBe(200);
210-
expect(test.body.data).toMatchObject({ ok: true, introspection: 'unsupported' });
211-
expect(test.body.data).not.toHaveProperty('error');
213+
expect(test.body.data).toMatchObject({ ok: true });
214+
expect(typeof test.body.data.latencyMs).toBe('number');
215+
// The answer's existing keys, and no other: no `tableCount` (it cannot list
216+
// tables), no `error`, and no key invented to say why.
217+
expect(Object.keys(test.body.data).sort()).toEqual(['latencyMs', 'ok']);
212218
});
213219

214220
it('a registered driver that fails its health check is not reported connected', async () => {
@@ -241,7 +247,6 @@ describe('nothing else moves', () => {
241247
const test = await call('POST', '/warehouse/test');
242248
expect(test.status).toBe(200);
243249
expect(test.body.data).toMatchObject({ ok: true, tableCount: 1 });
244-
expect(test.body.data).not.toHaveProperty('introspection');
245250

246251
const tables = await call('GET', '/warehouse/remote-tables');
247252
expect(tables.status).toBe(200);

‎packages/services/service-datasource/src/external-datasource-service.ts‎

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -569,22 +569,19 @@ export class ExternalDatasourceService implements IExternalDatasourceService {
569569
* name (ADR-0062), and it is not a federation source, so its tables are not
570570
* introspected: that costs a round trip per table of the platform database
571571
* and answers nothing the connection question needs. No `tableCount`.
572-
* - a driver with no `introspectSchema` (memory, mongodb). The answer says
573-
* so in `introspection: 'unsupported'`, rather than calling a connected
574-
* datasource failed.
572+
* - a driver with no `introspectSchema` (memory, mongodb). Its connected
573+
* datasource was called failed. It now answers the same health probe, and
574+
* the missing `tableCount` is the whole of what it cannot say.
575575
*
576-
* Every other datasource is tested exactly as before. So is one with no live
577-
* driver: `introspect` names the absence itself.
576+
* Both answer `{ ok, latencyMs }`, the answer's existing keys: no key is added
577+
* to say why `tableCount` is absent, because nothing reads one (the console's
578+
* Test reads `ok`, `error` and `latencyMs`). Every other datasource is tested
579+
* exactly as before. So is one with no live driver: `introspect` names the
580+
* absence itself.
578581
*/
579582
async testConnection(
580583
datasource: string,
581-
): Promise<{
582-
ok: boolean;
583-
latencyMs?: number;
584-
tableCount?: number;
585-
introspection?: 'unsupported';
586-
error?: string;
587-
}> {
584+
): Promise<{ ok: boolean; latencyMs?: number; tableCount?: number; error?: string }> {
588585
const started = Date.now();
589586
try {
590587
const driver = this.config.liveDriver?.(datasource);
@@ -593,11 +590,7 @@ export class ExternalDatasourceService implements IExternalDatasourceService {
593590
if (!(await driver.checkHealth())) {
594591
return { ok: false, error: `Datasource '${datasource}' is registered, but its driver failed its health check.` };
595592
}
596-
return {
597-
ok: true,
598-
latencyMs: Date.now() - started,
599-
...(canIntrospect ? {} : { introspection: 'unsupported' as const }),
600-
};
593+
return { ok: true, latencyMs: Date.now() - started };
601594
}
602595
const schema = await this.config.introspect(datasource);
603596
return {

0 commit comments

Comments
 (0)