Line data Source code
1 : import { ConnectionDispatch } from './connection_registry.js';
2 : import { HEALTH_CHECK_CANCELLED, HEALTH_CHECK_FAILED, HEALTH_CHECK_STARTED, HEALTH_CHECK_SUCCEEDED } from './connection_state.js';
3 : import type { DetailedError } from '../utils/error.js';
4 : import { HyperDatabaseChannel } from './hyper/hyperdb_grpc_client.js';
5 : import { QueryExecutor } from './query_executor.js';
6 : import { QueryType } from '../query/query_execution_state.js';
7 : import { SalesforceDatabaseChannel } from './salesforce/salesforce_api_client.js';
8 : import { TrinoChannelInterface } from './trino/trino_channel.js';
9 :
10 : export type HealthCheckChannel =
11 : | { type: 'hyper'; channel: HyperDatabaseChannel }
12 : | { type: 'salesforce'; channel: SalesforceDatabaseChannel }
13 : | { type: 'trino'; channel: TrinoChannelInterface };
14 :
15 1 : function pickProbeQuery(channel: HealthCheckChannel): string {
16 1 : switch (channel.type) {
17 1 : case 'trino':
18 1 : return 'select 1';
19 1 : case 'hyper':
20 1 : case 'salesforce':
21 1 : return 'select 1 as healthy';
22 1 : }
23 1 : }
24 :
25 : function toDetailedError(error: unknown): DetailedError {
26 : if (error instanceof Error) {
27 : return { message: error.message };
28 : }
29 : if (error != null && typeof error === 'object' && 'message' in error && typeof (error as { message: unknown }).message === 'string') {
30 : return { message: (error as { message: string }).message };
31 : }
32 : return { message: String(error ?? 'health check failed') };
33 : }
34 :
35 : /// Run the connection's health check through the query executor so the probe shows up
36 : /// in the internals query viewer like any other query. The caller passes the channel
37 : /// it already holds locally after setup — we don't wait for the React state update
38 : /// that publishes the channel into the connection map.
39 : ///
40 : /// Dispatches HEALTH_CHECK_STARTED before the first await, then maps the outcome to
41 : /// HEALTH_CHECK_SUCCEEDED / FAILED / CANCELLED and rethrows on non-success so callers
42 : /// keep their existing try/catch semantics.
43 1 : export async function performHealthCheck(
44 1 : executor: QueryExecutor,
45 1 : connectionId: string,
46 1 : channel: HealthCheckChannel,
47 1 : dispatch: ConnectionDispatch,
48 1 : abortSignal: AbortSignal,
49 1 : ): Promise<void> {
50 1 : dispatch({ type: HEALTH_CHECK_STARTED, value: null });
51 1 : abortSignal.throwIfAborted();
52 :
53 1 : const [, execution] = executor(connectionId, {
54 1 : query: pickProbeQuery(channel),
55 1 : analyzeResults: false,
56 1 : metadata: {
57 1 : queryType: QueryType.HEALTH_CHECK,
58 1 : title: 'Health Check',
59 1 : description: null,
60 1 : issuer: 'Connection Setup',
61 1 : userProvided: false,
62 1 : },
63 1 : channelOverride: channel,
64 1 : });
65 1 : const table = await execution;
66 :
67 1 : if (abortSignal.aborted) {
68 0 : dispatch({ type: HEALTH_CHECK_CANCELLED, value: null });
69 0 : const abortError: Error & { name: string } = new Error('health check cancelled');
70 0 : abortError.name = 'AbortError';
71 0 : throw abortError;
72 0 : }
73 1 : if (table == null) {
74 0 : const detailed: DetailedError = { message: 'health check failed' };
75 0 : dispatch({ type: HEALTH_CHECK_FAILED, value: detailed });
76 0 : throw new Error(detailed.message);
77 0 : }
78 1 : dispatch({ type: HEALTH_CHECK_SUCCEEDED, value: null });
79 1 : }
|