@deephaven-enterprise/query-utils 2026.1.26-- → 2026.1.28--
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CorePlusManager.d.ts +47 -0
- package/dist/CorePlusManager.js +1 -0
- package/dist/QueryStatus.d.ts +19 -0
- package/dist/QueryStatus.js +31 -0
- package/dist/QueryUtils.d.ts +277 -0
- package/dist/QueryUtils.js +553 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/package.json +2 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { EnterpriseClient, WorkerKind } from '@deephaven-enterprise/jsapi-types';
|
|
2
|
+
import type { dh as DhType } from '@deephaven/jsapi-types';
|
|
3
|
+
/**
|
|
4
|
+
* Interface defining a connection manager for fetching APIs from and creating connections to CorePlus workers. Implementation may cache values.
|
|
5
|
+
*/
|
|
6
|
+
export interface CorePlusManager {
|
|
7
|
+
/** The DHE JS API client */
|
|
8
|
+
get dheClient(): EnterpriseClient;
|
|
9
|
+
/** Whether this has been disposed. */
|
|
10
|
+
get isDisposed(): boolean;
|
|
11
|
+
/** Dispose resources. */
|
|
12
|
+
dispose(): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Get the API for the specified worker kind.
|
|
15
|
+
* @param engine The engine to get the API for
|
|
16
|
+
* @param jsApiUrl The URL to load the API from
|
|
17
|
+
* @return The API for the specified worker kind
|
|
18
|
+
*/
|
|
19
|
+
getApi(engine: string, jsApiUrl: string): Promise<typeof DhType>;
|
|
20
|
+
/**
|
|
21
|
+
* Get the CoreClient for the specified worker.
|
|
22
|
+
* @param dh The Core API to create the CoreClient with.
|
|
23
|
+
* @param grpcUrl The gRPC URL
|
|
24
|
+
* @param envoyPrefix The envoy prefix
|
|
25
|
+
* @return The CoreClient for the specified worker kind
|
|
26
|
+
*/
|
|
27
|
+
getClient(dh: typeof DhType, grpcUrl: string, envoyPrefix?: string | null): Promise<DhType.CoreClient>;
|
|
28
|
+
/**
|
|
29
|
+
* Get the IDE connection for the specified worker.
|
|
30
|
+
* @param dh The Core API to create the CoreClient with.
|
|
31
|
+
* @param grpcUrl The gRPC URL
|
|
32
|
+
* @param envoyPrefix The envoy prefix
|
|
33
|
+
* @return The IDE connection for the specified worker kind
|
|
34
|
+
*/
|
|
35
|
+
getConnection(dh: typeof DhType, grpcUrl: string, envoyPrefix?: string | null): Promise<DhType.IdeConnection>;
|
|
36
|
+
/**
|
|
37
|
+
* Get the worker kind or null for the specified engine.
|
|
38
|
+
* @param engine The engine to get the worker kind for
|
|
39
|
+
*/
|
|
40
|
+
getEngineWorkerKind(engine: string): WorkerKind | null;
|
|
41
|
+
/**
|
|
42
|
+
* Check if an engine is a community worker kind.
|
|
43
|
+
* @param engine The engine name to check
|
|
44
|
+
*/
|
|
45
|
+
isCommunityWorkerKind(engine: string | null): boolean;
|
|
46
|
+
}
|
|
47
|
+
export default CorePlusManager;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare class QueryStatus {
|
|
2
|
+
static getDisplayString(status?: string | null): string;
|
|
3
|
+
static getClassName(status?: string | null): string;
|
|
4
|
+
static uninitialized: string;
|
|
5
|
+
static connecting: string;
|
|
6
|
+
static authenticating: string;
|
|
7
|
+
static acquiringWorker: string;
|
|
8
|
+
static initializing: string;
|
|
9
|
+
static running: string;
|
|
10
|
+
static failed: string;
|
|
11
|
+
static error: string;
|
|
12
|
+
static disconnected: string;
|
|
13
|
+
static stopping: string;
|
|
14
|
+
static stopped: string;
|
|
15
|
+
static completed: string;
|
|
16
|
+
static executing: string;
|
|
17
|
+
static none: string;
|
|
18
|
+
}
|
|
19
|
+
export default QueryStatus;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const NULL_STATUS_STRING = 'None';
|
|
2
|
+
export class QueryStatus {
|
|
3
|
+
// returns a humanized name from status
|
|
4
|
+
static getDisplayString(status) {
|
|
5
|
+
return status != null && status !== QueryStatus.none
|
|
6
|
+
? status.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
7
|
+
: NULL_STATUS_STRING;
|
|
8
|
+
}
|
|
9
|
+
static getClassName(status) {
|
|
10
|
+
return (status != null && status !== QueryStatus.none
|
|
11
|
+
? status.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
12
|
+
: NULL_STATUS_STRING).toLowerCase();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
QueryStatus.uninitialized = 'Uninitialized';
|
|
16
|
+
QueryStatus.connecting = 'Connecting';
|
|
17
|
+
QueryStatus.authenticating = 'Authenticating';
|
|
18
|
+
QueryStatus.acquiringWorker = 'AcquiringWorker';
|
|
19
|
+
QueryStatus.initializing = 'Initializing';
|
|
20
|
+
QueryStatus.running = 'Running';
|
|
21
|
+
QueryStatus.failed = 'Failed';
|
|
22
|
+
QueryStatus.error = 'Error';
|
|
23
|
+
QueryStatus.disconnected = 'Disconnected';
|
|
24
|
+
QueryStatus.stopping = 'Stopping';
|
|
25
|
+
QueryStatus.stopped = 'Stopped';
|
|
26
|
+
QueryStatus.completed = 'Completed';
|
|
27
|
+
QueryStatus.executing = 'Executing';
|
|
28
|
+
// Some queries have a null status which is translated to an empty string for JS
|
|
29
|
+
// This is not an official status but is listed here for clarity
|
|
30
|
+
QueryStatus.none = '';
|
|
31
|
+
export default QueryStatus;
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import type { dh } from '@deephaven/jsapi-types';
|
|
2
|
+
import type { UriVariableDescriptor } from '@deephaven/jsapi-bootstrap';
|
|
3
|
+
import type { EnterpriseClient, EnterpriseDhType, QueryInfo, QueryVariableDescriptor, ReplicaStatus, UserInfo } from '@deephaven-enterprise/jsapi-types';
|
|
4
|
+
import { type Brand } from '@deephaven/utils';
|
|
5
|
+
import DraftQuery, { type DraftQueryConstructorObject } from './DraftQuery';
|
|
6
|
+
import type { CorePlusManager } from './CorePlusManager';
|
|
7
|
+
export type ConsoleType = 'groovy' | 'python';
|
|
8
|
+
export type QuerySerial = Brand<'QuerySerial', string>;
|
|
9
|
+
export declare const DEFAULT_TEMPORARY_QUERY_AUTO_DELETE_TIMEOUT_MS: 600000;
|
|
10
|
+
export declare const DEFAULT_TEMPORARY_QUERY_TIMEOUT_MS: 60000;
|
|
11
|
+
export declare const INTERACTIVE_CONSOLE_TEMPORARY_QUEUE_NAME: "InteractiveConsoleTemporaryQueue";
|
|
12
|
+
export declare const QUERY_TIMEOUT: 10000;
|
|
13
|
+
export declare const MESSAGE_TIMEOUT: 10000;
|
|
14
|
+
/** Configuration type for the Core+ WebClientData query */
|
|
15
|
+
export declare const WEB_CLIENT_DATA_CORE_CONFIG_TYPE: "WebClientData";
|
|
16
|
+
export declare const WEB_CLIENT_DATA_CORE_QUERY: "WebClientData";
|
|
17
|
+
export declare const WEB_CLIENT_TABLE_FACTORY_TYPE: "WebClientTableFactoryService";
|
|
18
|
+
export declare const WEB_CLIENT_TABLE_FACTORY_NAME: "WebClientTableFactory";
|
|
19
|
+
export declare const WEB_CLIENT_REVERT_TABLE_TYPE: "RevertTableProviderService";
|
|
20
|
+
export declare const WEB_CLIENT_REVERT_TABLE_NAME: "RevertTableProvider";
|
|
21
|
+
export declare const WEB_CLIENT_WRITE_OBJECT_NAME: "WorkspaceDataWriter";
|
|
22
|
+
export declare const WEB_CLIENT_WRITE_OBJECT_TYPE: "WorkspaceDataWriterService";
|
|
23
|
+
export declare const QUERY_CONFIG_TABLE: "QueryInfo";
|
|
24
|
+
export declare const SCOPE_CONFIG_TABLE: "ScopeNamesAndType";
|
|
25
|
+
export declare const CATALOG_TABLE: "catalog";
|
|
26
|
+
export declare const FACTORY_SERVICE_TABLES: Set<"QueryInfo" | "ScopeNamesAndType" | "catalog">;
|
|
27
|
+
export type TemporaryDraftQueryConfig = Omit<DraftQueryConstructorObject, 'scheduling' | 'workerKind' | 'type'> & {
|
|
28
|
+
type: string;
|
|
29
|
+
engine: string;
|
|
30
|
+
isCommunityWorker?: boolean;
|
|
31
|
+
};
|
|
32
|
+
export type WebClientData2Response = {
|
|
33
|
+
id: string;
|
|
34
|
+
error: string | null;
|
|
35
|
+
};
|
|
36
|
+
export type WebClientData2WriteResponse = WebClientData2Response & {
|
|
37
|
+
uuids: string[];
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Creates a temporary draft query with auto-delete enabled.
|
|
41
|
+
*
|
|
42
|
+
* Temporary queries are automatically scheduled on the InteractiveConsoleTemporaryQueue
|
|
43
|
+
* and will be deleted when they complete or timeout. This is useful for queries that
|
|
44
|
+
* should only run once and be cleaned up automatically.
|
|
45
|
+
*
|
|
46
|
+
* @param config Configuration object for the temporary draft query
|
|
47
|
+
* @param config.additionalMemory Additional memory in MB to allocate beyond the heap size (default: 0)
|
|
48
|
+
* @param config.engine The engine/worker kind to use for the query
|
|
49
|
+
* @param config.heapSize JVM heap size multiplier (default: 0.5)
|
|
50
|
+
* @param config.isClientSide Whether this is a client-side query (default: true)
|
|
51
|
+
* @param config.isCommunityWorker Whether this is a Community (Core+) worker (default: false)
|
|
52
|
+
* @param config.initializationThreads Number of threads for initialization (Core+ only)
|
|
53
|
+
* @param config.updateThreads Number of threads for updates (Core+ only)
|
|
54
|
+
* @param config.timeout Timeout in milliseconds before auto-deletion (default: 60000)
|
|
55
|
+
* @returns A configured DraftQuery instance ready to be created
|
|
56
|
+
*/
|
|
57
|
+
export declare function createTemporaryDraftQuery({ additionalMemory, engine, heapSize, isClientSide, isCommunityWorker, initializationThreads, updateThreads, timeout, ...config }: TemporaryDraftQueryConfig): DraftQuery;
|
|
58
|
+
/**
|
|
59
|
+
* Gets the Core+ connection for a given query.
|
|
60
|
+
*
|
|
61
|
+
* @param params Function parameters
|
|
62
|
+
* @param params.query Query to get the connection for
|
|
63
|
+
* @param params.corePlusManager CorePlus manager for worker connections
|
|
64
|
+
* @returns the Core+ connection for the query
|
|
65
|
+
* @throws Error if the query is not running or not on a Core+ worker
|
|
66
|
+
*/
|
|
67
|
+
export declare function getCoreConnection({ query, corePlusManager, }: {
|
|
68
|
+
query: QueryInfo;
|
|
69
|
+
corePlusManager: CorePlusManager;
|
|
70
|
+
}): Promise<dh.IdeConnection>;
|
|
71
|
+
/**
|
|
72
|
+
* Get QueryInfo for a string which may be the query name or its serial.
|
|
73
|
+
* @param params Function parameters
|
|
74
|
+
* @param params.client Enterprise client to use
|
|
75
|
+
* @param params.dh Deephaven Enterprise API
|
|
76
|
+
* @param params.queryNameOrSerial String representing either the query name or serial, but we don't know which it is.
|
|
77
|
+
* @returns QueryInfo for the specified query
|
|
78
|
+
* @throws Error if the query is not found
|
|
79
|
+
*/
|
|
80
|
+
export declare function getQueryInfoForNameOrSerial({ client, dh, queryNameOrSerial, }: {
|
|
81
|
+
client: EnterpriseClient;
|
|
82
|
+
dh: EnterpriseDhType;
|
|
83
|
+
queryNameOrSerial: string;
|
|
84
|
+
}): Promise<QueryInfo>;
|
|
85
|
+
/**
|
|
86
|
+
* Get ObjectDefinition from a query by object name
|
|
87
|
+
* @param query Query
|
|
88
|
+
* @param name The name of the object to fetch
|
|
89
|
+
* @returns Resolves to the object definition
|
|
90
|
+
*/
|
|
91
|
+
export declare function getQueryObjectDefinitionByName(query: QueryInfo, name: string): dh.ide.VariableDefinition;
|
|
92
|
+
/**
|
|
93
|
+
* Get replica or spare worker for a given slot. If slot is null, returns the designated worker.
|
|
94
|
+
* @param query Query to get the worker from
|
|
95
|
+
* @param slot The slot to get the worker for
|
|
96
|
+
* @returns Resolves to the ReplicaStatus
|
|
97
|
+
*/
|
|
98
|
+
export declare function getWorkerForSlot(query: QueryInfo, slot?: number | null): ReplicaStatus | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* Indicates that a table should be fetched from the WebClientTableFactoryService rather than the query scope.
|
|
101
|
+
*
|
|
102
|
+
* @param tableName the name of the table
|
|
103
|
+
* @returns true if the table is a factory service table, false otherwise
|
|
104
|
+
*/
|
|
105
|
+
export declare function isFactoryServiceTable(tableName: string): boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Turns a URI into a QueryVariableDescriptor
|
|
108
|
+
* @param params Function parameters
|
|
109
|
+
* @param params.client Enterprise client to use
|
|
110
|
+
* @param params.dh Deephaven Enterprise API
|
|
111
|
+
* @param params.uri The URI to convert to a descriptor
|
|
112
|
+
* @returns A promise that resolves to the query variable descriptor
|
|
113
|
+
* @throws Error if the URI is invalid, not found, or an unsupported protocol
|
|
114
|
+
*/
|
|
115
|
+
export declare function makeDescriptorFromUri({ client, dh, uri, }: {
|
|
116
|
+
client: EnterpriseClient;
|
|
117
|
+
dh: EnterpriseDhType;
|
|
118
|
+
uri: UriVariableDescriptor;
|
|
119
|
+
}): Promise<QueryVariableDescriptor>;
|
|
120
|
+
/**
|
|
121
|
+
* Get a table from the WebClientData Core+ WebClientTableFactoryService
|
|
122
|
+
* @param params Function parameters
|
|
123
|
+
* @param params.client Enterprise client
|
|
124
|
+
* @param params.corePlusManager CorePlus manager for worker connections
|
|
125
|
+
* @param params.dh Deephaven Enterprise API
|
|
126
|
+
* @param params.userInfo User information
|
|
127
|
+
* @param params.tableName Table name to get
|
|
128
|
+
* @returns Promise for the table
|
|
129
|
+
*/
|
|
130
|
+
export declare function makeFactoryServiceTablePromise({ client, corePlusManager, dh, userInfo, tableName, }: {
|
|
131
|
+
client: EnterpriseClient;
|
|
132
|
+
corePlusManager: CorePlusManager;
|
|
133
|
+
dh: EnterpriseDhType;
|
|
134
|
+
userInfo: UserInfo;
|
|
135
|
+
tableName?: string;
|
|
136
|
+
}): Promise<dh.Table>;
|
|
137
|
+
/**
|
|
138
|
+
* Creates PQ Query Promise
|
|
139
|
+
* @param params Function parameters
|
|
140
|
+
* @param params.client Enterprise client to use
|
|
141
|
+
* @param params.dh Deephaven Enterprise API
|
|
142
|
+
* @param params.queryName Query name
|
|
143
|
+
* @param params.querySerial Serial to check - if present, will use this instead of name to match
|
|
144
|
+
* @returns A promise for the query
|
|
145
|
+
*/
|
|
146
|
+
export declare function makeQueryPromise({ client, dh, queryName, querySerial, }: {
|
|
147
|
+
client: EnterpriseClient;
|
|
148
|
+
dh: EnterpriseDhType;
|
|
149
|
+
queryName: string | undefined;
|
|
150
|
+
querySerial?: string | null;
|
|
151
|
+
}): Promise<QueryInfo>;
|
|
152
|
+
/**
|
|
153
|
+
* Creates PQ Query Object Promise
|
|
154
|
+
* @param params Function parameters
|
|
155
|
+
* @param params.client Enterprise client to use
|
|
156
|
+
* @param params.corePlusManager CorePlus manager for worker connections
|
|
157
|
+
* @param params.dh Deephaven Enterprise API
|
|
158
|
+
* @param params.queryName Query name to fetch
|
|
159
|
+
* @param params.querySerial Serial to check - if present, will use this instead of name to match
|
|
160
|
+
* @param params.objectDescriptor The descriptor of the object to fetch
|
|
161
|
+
* @param params.replicaSlot The replica slot to use, if null will use the designated worker
|
|
162
|
+
* @returns Resolves to the object fetched
|
|
163
|
+
*/
|
|
164
|
+
export declare function makeQueryObjectPromise<T = unknown>({ client, corePlusManager, dh, queryName, querySerial, objectDescriptor, replicaSlot, }: {
|
|
165
|
+
client: EnterpriseClient;
|
|
166
|
+
corePlusManager: CorePlusManager;
|
|
167
|
+
dh: EnterpriseDhType;
|
|
168
|
+
queryName: string | null | undefined;
|
|
169
|
+
querySerial: string | null | undefined;
|
|
170
|
+
objectDescriptor: dh.ide.VariableDescriptor;
|
|
171
|
+
replicaSlot?: number;
|
|
172
|
+
}): Promise<T>;
|
|
173
|
+
/**
|
|
174
|
+
* Creates PQ Table Promise
|
|
175
|
+
* Should use makeQueryObjectPromise when possible instead.
|
|
176
|
+
* @param params Function parameters
|
|
177
|
+
* @param params.client Enterprise client
|
|
178
|
+
* @param params.corePlusManager CorePlus manager for worker connections
|
|
179
|
+
* @param params.dh Deephaven Enterprise API
|
|
180
|
+
* @param params.queryName Query name
|
|
181
|
+
* @param params.querySerial Query serial
|
|
182
|
+
* @param params.tableName Table name
|
|
183
|
+
* @returns A promise for the table or tree table
|
|
184
|
+
*/
|
|
185
|
+
export declare function makeQueryTablePromise({ client, corePlusManager, dh, queryName, querySerial, tableName, }: {
|
|
186
|
+
client: EnterpriseClient;
|
|
187
|
+
corePlusManager: CorePlusManager;
|
|
188
|
+
dh: EnterpriseDhType;
|
|
189
|
+
queryName: string;
|
|
190
|
+
querySerial: string | null | undefined;
|
|
191
|
+
tableName: string;
|
|
192
|
+
}): Promise<dh.Table | dh.TreeTable>;
|
|
193
|
+
/**
|
|
194
|
+
* Retrieves a table with the revision history for a given query.
|
|
195
|
+
* @param params Function parameters
|
|
196
|
+
* @param params.client Enterprise client
|
|
197
|
+
* @param params.corePlusManager CorePlus manager for worker connections
|
|
198
|
+
* @param params.dh Deephaven Enterprise API
|
|
199
|
+
* @param params.serial Serial of the query to get the revision history for.
|
|
200
|
+
* @returns A promise for the revision history table.
|
|
201
|
+
*/
|
|
202
|
+
export declare function makeRevertTablePromise({ client, corePlusManager, dh, serial, }: {
|
|
203
|
+
client: EnterpriseClient;
|
|
204
|
+
corePlusManager: CorePlusManager;
|
|
205
|
+
dh: EnterpriseDhType;
|
|
206
|
+
serial: string;
|
|
207
|
+
}): Promise<dh.Table>;
|
|
208
|
+
/**
|
|
209
|
+
* Sends a message to a WebClientData2 widget and returns a promise that resolves to the event detail.
|
|
210
|
+
*
|
|
211
|
+
* @param params Function parameters
|
|
212
|
+
* @param params.client Enterprise client
|
|
213
|
+
* @param params.corePlusManager CorePlus manager for worker connections
|
|
214
|
+
* @param params.dh Deephaven Enterprise API
|
|
215
|
+
* @param params.messageId the ID of the message to send
|
|
216
|
+
* @param params.message the message to send
|
|
217
|
+
* @param params.type the type of the widget to send the message to
|
|
218
|
+
* @param params.name the name of the widget to send the message to
|
|
219
|
+
* @returns the event detail from the response
|
|
220
|
+
*/
|
|
221
|
+
export declare function sendWebClientData2Message<T extends WebClientData2Response>({ client, corePlusManager, dh, messageId, message, type, name, }: {
|
|
222
|
+
client: EnterpriseClient;
|
|
223
|
+
corePlusManager: CorePlusManager;
|
|
224
|
+
dh: EnterpriseDhType;
|
|
225
|
+
messageId: string;
|
|
226
|
+
message: string;
|
|
227
|
+
type: string;
|
|
228
|
+
name: string;
|
|
229
|
+
}): Promise<[T, dh.WidgetMessageDetails]>;
|
|
230
|
+
/**
|
|
231
|
+
* Sends a message to a widget on a Core+ query. Will call the provided callback with the event.
|
|
232
|
+
* It is the responsibility of the calling code to determine if the message is correct and to clean up the event listener.
|
|
233
|
+
* This method will time out after a set period if the message is not received.
|
|
234
|
+
*
|
|
235
|
+
* TODO: DH-20345: There are a number of issues with this function not properly
|
|
236
|
+
* handling Promise rejections and timeouts. Since it returns `void`, there is
|
|
237
|
+
* no way for an upstream caller to handle errors or even know about them.
|
|
238
|
+
*
|
|
239
|
+
* @param params Function parameters
|
|
240
|
+
* @param params.client Enterprise client
|
|
241
|
+
* @param params.corePlusManager CorePlus manager for worker connections
|
|
242
|
+
* @param params.dh Deephaven Enterprise API
|
|
243
|
+
* @param params.message the message to send
|
|
244
|
+
* @param params.onEventCallback the callback to handle the event when the message is received
|
|
245
|
+
* @param params.queryName the name of the query to send the message to, null if using serial
|
|
246
|
+
* @param params.querySerial the serial of the query to send the message to, null if using name
|
|
247
|
+
* @param params.objectDefinition the definition of the widget to send the message to
|
|
248
|
+
* @param params.replicaSlot the replica slot to send the message to, if null will use the designated worker
|
|
249
|
+
*/
|
|
250
|
+
export declare function sendWidgetMessage({ client, corePlusManager, dh, message, onEventCallback, queryName, querySerial, objectDefinition, replicaSlot, }: {
|
|
251
|
+
client: EnterpriseClient;
|
|
252
|
+
corePlusManager: CorePlusManager;
|
|
253
|
+
dh: EnterpriseDhType;
|
|
254
|
+
message: string;
|
|
255
|
+
onEventCallback: (event: dh.Event<dh.WidgetMessageDetails>, cleanupFn: () => void) => void;
|
|
256
|
+
queryName: string | null | undefined;
|
|
257
|
+
querySerial: string | null | undefined;
|
|
258
|
+
objectDefinition: dh.ide.VariableDescriptor;
|
|
259
|
+
replicaSlot?: number;
|
|
260
|
+
}): void;
|
|
261
|
+
/**
|
|
262
|
+
* Starts a query and returns a promise that resolves when the query starts running.
|
|
263
|
+
*
|
|
264
|
+
* This function:
|
|
265
|
+
* - Creates the query on the server
|
|
266
|
+
* - Listens for status updates
|
|
267
|
+
* - Auto-starts temporary queries (which don't start automatically)
|
|
268
|
+
* - Resolves when the query reaches running status
|
|
269
|
+
* - Rejects if the query fails to start
|
|
270
|
+
*
|
|
271
|
+
* @param dh Deephaven Enterprise API
|
|
272
|
+
* @param client Enterprise client to create the query with
|
|
273
|
+
* @param draftQuery Draft query configuration to start
|
|
274
|
+
* @returns Promise that resolves to the QueryInfo when the query starts running
|
|
275
|
+
* @throws Error if the query fails to start or encounters an error
|
|
276
|
+
*/
|
|
277
|
+
export declare function startQuery(dh: EnterpriseDhType, client: EnterpriseClient, draftQuery: DraftQuery): Promise<QueryInfo>;
|
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { InvalidMetadataError } from '@deephaven/utils';
|
|
13
|
+
import Log from '@deephaven/log';
|
|
14
|
+
import { nanoid } from 'nanoid';
|
|
15
|
+
import QueryScheduler from './QueryScheduler';
|
|
16
|
+
import DraftQuery from './DraftQuery';
|
|
17
|
+
import { WorkerThreadUtils } from './WorkerThreadUtils';
|
|
18
|
+
import { QueryStatus } from './QueryStatus';
|
|
19
|
+
const log = Log.module('QueryUtils');
|
|
20
|
+
// 600 seconds is based on default `auto_delete_timeout` in
|
|
21
|
+
// `ControllerClient.make_temporary_config`
|
|
22
|
+
export const DEFAULT_TEMPORARY_QUERY_AUTO_DELETE_TIMEOUT_MS = 600000;
|
|
23
|
+
export const DEFAULT_TEMPORARY_QUERY_TIMEOUT_MS = 60000;
|
|
24
|
+
export const INTERACTIVE_CONSOLE_TEMPORARY_QUEUE_NAME = 'InteractiveConsoleTemporaryQueue';
|
|
25
|
+
// Query interaction constants
|
|
26
|
+
export const QUERY_TIMEOUT = 10000;
|
|
27
|
+
export const MESSAGE_TIMEOUT = 10000;
|
|
28
|
+
/** Configuration type for the Core+ WebClientData query */
|
|
29
|
+
export const WEB_CLIENT_DATA_CORE_CONFIG_TYPE = 'WebClientData';
|
|
30
|
+
// Name of the Core+ WebClientData query. Used for reading and writing workspace data.
|
|
31
|
+
export const WEB_CLIENT_DATA_CORE_QUERY = 'WebClientData';
|
|
32
|
+
export const WEB_CLIENT_TABLE_FACTORY_TYPE = 'WebClientTableFactoryService';
|
|
33
|
+
export const WEB_CLIENT_TABLE_FACTORY_NAME = 'WebClientTableFactory';
|
|
34
|
+
export const WEB_CLIENT_REVERT_TABLE_TYPE = 'RevertTableProviderService';
|
|
35
|
+
export const WEB_CLIENT_REVERT_TABLE_NAME = 'RevertTableProvider';
|
|
36
|
+
export const WEB_CLIENT_WRITE_OBJECT_NAME = 'WorkspaceDataWriter';
|
|
37
|
+
export const WEB_CLIENT_WRITE_OBJECT_TYPE = 'WorkspaceDataWriterService';
|
|
38
|
+
export const QUERY_CONFIG_TABLE = 'QueryInfo';
|
|
39
|
+
export const SCOPE_CONFIG_TABLE = 'ScopeNamesAndType';
|
|
40
|
+
export const CATALOG_TABLE = 'catalog';
|
|
41
|
+
export const FACTORY_SERVICE_TABLES = new Set([
|
|
42
|
+
QUERY_CONFIG_TABLE,
|
|
43
|
+
SCOPE_CONFIG_TABLE,
|
|
44
|
+
CATALOG_TABLE,
|
|
45
|
+
]);
|
|
46
|
+
/**
|
|
47
|
+
* Creates a temporary draft query with auto-delete enabled.
|
|
48
|
+
*
|
|
49
|
+
* Temporary queries are automatically scheduled on the InteractiveConsoleTemporaryQueue
|
|
50
|
+
* and will be deleted when they complete or timeout. This is useful for queries that
|
|
51
|
+
* should only run once and be cleaned up automatically.
|
|
52
|
+
*
|
|
53
|
+
* @param config Configuration object for the temporary draft query
|
|
54
|
+
* @param config.additionalMemory Additional memory in MB to allocate beyond the heap size (default: 0)
|
|
55
|
+
* @param config.engine The engine/worker kind to use for the query
|
|
56
|
+
* @param config.heapSize JVM heap size multiplier (default: 0.5)
|
|
57
|
+
* @param config.isClientSide Whether this is a client-side query (default: true)
|
|
58
|
+
* @param config.isCommunityWorker Whether this is a Community (Core+) worker (default: false)
|
|
59
|
+
* @param config.initializationThreads Number of threads for initialization (Core+ only)
|
|
60
|
+
* @param config.updateThreads Number of threads for updates (Core+ only)
|
|
61
|
+
* @param config.timeout Timeout in milliseconds before auto-deletion (default: 60000)
|
|
62
|
+
* @returns A configured DraftQuery instance ready to be created
|
|
63
|
+
*/
|
|
64
|
+
export function createTemporaryDraftQuery(_a) {
|
|
65
|
+
var { additionalMemory = 0, engine, heapSize = 0.5, isClientSide = true, isCommunityWorker = false, initializationThreads, updateThreads, timeout = DEFAULT_TEMPORARY_QUERY_TIMEOUT_MS } = _a, config = __rest(_a, ["additionalMemory", "engine", "heapSize", "isClientSide", "isCommunityWorker", "initializationThreads", "updateThreads", "timeout"]);
|
|
66
|
+
const scheduling = QueryScheduler.makeTemporaryScheduling({
|
|
67
|
+
autoDelete: true,
|
|
68
|
+
queueName: INTERACTIVE_CONSOLE_TEMPORARY_QUEUE_NAME,
|
|
69
|
+
});
|
|
70
|
+
const draftQuery = new DraftQuery(Object.assign(Object.assign({}, config), { additionalMemory,
|
|
71
|
+
heapSize,
|
|
72
|
+
isClientSide,
|
|
73
|
+
scheduling,
|
|
74
|
+
timeout, workerKind: engine,
|
|
75
|
+
// TODO: DH-22081: createTemporaryDraftQuery converts initializationThreads/
|
|
76
|
+
// updateThreads with WorkerThreadUtils.getValue(...) before storing them on
|
|
77
|
+
// the DraftQuery. DraftQuery later calls WorkerThreadUtils.addArgs, which
|
|
78
|
+
// expects display strings
|
|
79
|
+
initializationThreads: isCommunityWorker && initializationThreads != null
|
|
80
|
+
? WorkerThreadUtils.getValue(initializationThreads)
|
|
81
|
+
: undefined, updateThreads: isCommunityWorker && updateThreads != null
|
|
82
|
+
? WorkerThreadUtils.getValue(updateThreads)
|
|
83
|
+
: undefined }));
|
|
84
|
+
draftQuery.addThreadCountToJVMArgs();
|
|
85
|
+
draftQuery.updateSchedule();
|
|
86
|
+
return draftQuery;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Gets the Core+ connection for a given query.
|
|
90
|
+
*
|
|
91
|
+
* @param params Function parameters
|
|
92
|
+
* @param params.query Query to get the connection for
|
|
93
|
+
* @param params.corePlusManager CorePlus manager for worker connections
|
|
94
|
+
* @returns the Core+ connection for the query
|
|
95
|
+
* @throws Error if the query is not running or not on a Core+ worker
|
|
96
|
+
*/
|
|
97
|
+
export function getCoreConnection({ query, corePlusManager, }) {
|
|
98
|
+
const designated = getWorkerForSlot(query);
|
|
99
|
+
if (designated == null || designated.status !== QueryStatus.running) {
|
|
100
|
+
throw new Error(`Query ${query.name} is not running.`);
|
|
101
|
+
}
|
|
102
|
+
// Sending a message to a widget requires a Core+ worker
|
|
103
|
+
if (!corePlusManager.isCommunityWorkerKind(query.workerKind)) {
|
|
104
|
+
throw new Error(`Query ${query.name} is not running on a Core+ worker.`);
|
|
105
|
+
}
|
|
106
|
+
const { grpcUrl, envoyPrefix, jsApiUrl } = designated;
|
|
107
|
+
return corePlusManager
|
|
108
|
+
.getApi(query.workerKind, jsApiUrl)
|
|
109
|
+
.then(api => corePlusManager.getConnection(api, grpcUrl, envoyPrefix));
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get QueryInfo for a string which may be the query name or its serial.
|
|
113
|
+
* @param params Function parameters
|
|
114
|
+
* @param params.client Enterprise client to use
|
|
115
|
+
* @param params.dh Deephaven Enterprise API
|
|
116
|
+
* @param params.queryNameOrSerial String representing either the query name or serial, but we don't know which it is.
|
|
117
|
+
* @returns QueryInfo for the specified query
|
|
118
|
+
* @throws Error if the query is not found
|
|
119
|
+
*/
|
|
120
|
+
export async function getQueryInfoForNameOrSerial({ client, dh, queryNameOrSerial, }) {
|
|
121
|
+
try {
|
|
122
|
+
return await makeQueryPromise({
|
|
123
|
+
client,
|
|
124
|
+
dh,
|
|
125
|
+
queryName: undefined,
|
|
126
|
+
querySerial: queryNameOrSerial,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
log.debug(`${queryNameOrSerial} not found as a serial. Trying as a name.`);
|
|
131
|
+
}
|
|
132
|
+
return makeQueryPromise({
|
|
133
|
+
client,
|
|
134
|
+
dh,
|
|
135
|
+
queryName: queryNameOrSerial,
|
|
136
|
+
querySerial: undefined,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Get ObjectDefinition from a query by object name
|
|
141
|
+
* @param query Query
|
|
142
|
+
* @param name The name of the object to fetch
|
|
143
|
+
* @returns Resolves to the object definition
|
|
144
|
+
*/
|
|
145
|
+
export function getQueryObjectDefinitionByName(query, name) {
|
|
146
|
+
var _a;
|
|
147
|
+
// Non-running queries don't list the objects
|
|
148
|
+
if (((_a = query.designated) === null || _a === void 0 ? void 0 : _a.status) !== QueryStatus.running) {
|
|
149
|
+
throw new Error(`Query ${query.name} isn't running.`);
|
|
150
|
+
}
|
|
151
|
+
const objectDefinition = query.designated.objects.find(o => o.title === name);
|
|
152
|
+
if (objectDefinition == null) {
|
|
153
|
+
throw new Error(`Object ${name} not found in query ${query.name}.`);
|
|
154
|
+
}
|
|
155
|
+
return objectDefinition;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get replica or spare worker for a given slot. If slot is null, returns the designated worker.
|
|
159
|
+
* @param query Query to get the worker from
|
|
160
|
+
* @param slot The slot to get the worker for
|
|
161
|
+
* @returns Resolves to the ReplicaStatus
|
|
162
|
+
*/
|
|
163
|
+
export function getWorkerForSlot(query, slot) {
|
|
164
|
+
if (slot == null) {
|
|
165
|
+
return query.designated;
|
|
166
|
+
}
|
|
167
|
+
if (slot < 0) {
|
|
168
|
+
return query.spares[-slot - 1];
|
|
169
|
+
}
|
|
170
|
+
return query.replicas[slot];
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Indicates that a table should be fetched from the WebClientTableFactoryService rather than the query scope.
|
|
174
|
+
*
|
|
175
|
+
* @param tableName the name of the table
|
|
176
|
+
* @returns true if the table is a factory service table, false otherwise
|
|
177
|
+
*/
|
|
178
|
+
export function isFactoryServiceTable(tableName) {
|
|
179
|
+
return FACTORY_SERVICE_TABLES.has(tableName);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Turns a URI into a QueryVariableDescriptor
|
|
183
|
+
* @param params Function parameters
|
|
184
|
+
* @param params.client Enterprise client to use
|
|
185
|
+
* @param params.dh Deephaven Enterprise API
|
|
186
|
+
* @param params.uri The URI to convert to a descriptor
|
|
187
|
+
* @returns A promise that resolves to the query variable descriptor
|
|
188
|
+
* @throws Error if the URI is invalid, not found, or an unsupported protocol
|
|
189
|
+
*/
|
|
190
|
+
export async function makeDescriptorFromUri({ client, dh, uri, }) {
|
|
191
|
+
var _a;
|
|
192
|
+
let url;
|
|
193
|
+
try {
|
|
194
|
+
url = new URL(uri);
|
|
195
|
+
}
|
|
196
|
+
catch (e) {
|
|
197
|
+
throw new Error(`Invalid URI: ${uri}. Did you forget to URI encode it?`);
|
|
198
|
+
}
|
|
199
|
+
const protocol = url.protocol.slice(0, -1); // Remove the trailing ':'
|
|
200
|
+
const querySerialOrName = decodeURIComponent(url.hostname);
|
|
201
|
+
const pathName = decodeURIComponent(url.pathname);
|
|
202
|
+
const widgetName = pathName.replace(/^\/scope\//, '');
|
|
203
|
+
if (protocol !== 'pq') {
|
|
204
|
+
throw new Error(`Unsupported URI protocol: ${protocol}`);
|
|
205
|
+
}
|
|
206
|
+
if (!pathName.startsWith('/scope/')) {
|
|
207
|
+
throw new Error(`Expected PQ URI path to start with /scope/. Got ${pathName}`);
|
|
208
|
+
}
|
|
209
|
+
const queryInfo = await getQueryInfoForNameOrSerial({
|
|
210
|
+
client,
|
|
211
|
+
dh,
|
|
212
|
+
queryNameOrSerial: querySerialOrName,
|
|
213
|
+
});
|
|
214
|
+
const variableDefinition = (_a = queryInfo.designated) === null || _a === void 0 ? void 0 : _a.objects.find(obj => obj.name === widgetName);
|
|
215
|
+
if (!variableDefinition) {
|
|
216
|
+
throw new Error(`Query ${querySerialOrName} (Serial: ${queryInfo.serial}) does not have widget named ${widgetName}`);
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
querySerial: queryInfo.serial,
|
|
220
|
+
query: queryInfo.name,
|
|
221
|
+
name: widgetName,
|
|
222
|
+
type: variableDefinition.type,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Get a table from the WebClientData Core+ WebClientTableFactoryService
|
|
227
|
+
* @param params Function parameters
|
|
228
|
+
* @param params.client Enterprise client
|
|
229
|
+
* @param params.corePlusManager CorePlus manager for worker connections
|
|
230
|
+
* @param params.dh Deephaven Enterprise API
|
|
231
|
+
* @param params.userInfo User information
|
|
232
|
+
* @param params.tableName Table name to get
|
|
233
|
+
* @returns Promise for the table
|
|
234
|
+
*/
|
|
235
|
+
export async function makeFactoryServiceTablePromise({ client, corePlusManager, dh, userInfo, tableName = QUERY_CONFIG_TABLE, }) {
|
|
236
|
+
// This message id is used to correlate the message sent to the workspace widget
|
|
237
|
+
const messageId = nanoid();
|
|
238
|
+
const message = JSON.stringify({
|
|
239
|
+
id: messageId,
|
|
240
|
+
user: userInfo.operateAs,
|
|
241
|
+
tableNames: [tableName],
|
|
242
|
+
});
|
|
243
|
+
const [, eventDetail] = await sendWebClientData2Message({
|
|
244
|
+
client,
|
|
245
|
+
corePlusManager,
|
|
246
|
+
dh,
|
|
247
|
+
messageId,
|
|
248
|
+
message,
|
|
249
|
+
type: WEB_CLIENT_TABLE_FACTORY_TYPE,
|
|
250
|
+
name: WEB_CLIENT_TABLE_FACTORY_NAME,
|
|
251
|
+
});
|
|
252
|
+
if (eventDetail.exportedObjects.length < 1) {
|
|
253
|
+
throw new Error('No objects exported from the widget');
|
|
254
|
+
}
|
|
255
|
+
const table = await eventDetail.exportedObjects[0].fetch();
|
|
256
|
+
return table;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Creates PQ Query Promise
|
|
260
|
+
* @param params Function parameters
|
|
261
|
+
* @param params.client Enterprise client to use
|
|
262
|
+
* @param params.dh Deephaven Enterprise API
|
|
263
|
+
* @param params.queryName Query name
|
|
264
|
+
* @param params.querySerial Serial to check - if present, will use this instead of name to match
|
|
265
|
+
* @returns A promise for the query
|
|
266
|
+
*/
|
|
267
|
+
export async function makeQueryPromise({ client, dh, queryName, querySerial, }) {
|
|
268
|
+
function getMatchingQuery(queries) {
|
|
269
|
+
return querySerial !== undefined
|
|
270
|
+
? queries.find(query => query.serial === querySerial)
|
|
271
|
+
: queries.find(query => query.name === queryName);
|
|
272
|
+
}
|
|
273
|
+
function waitForAddedQuery() {
|
|
274
|
+
return new Promise((resolve, reject) => {
|
|
275
|
+
let removeListener;
|
|
276
|
+
const timeout = setTimeout(() => {
|
|
277
|
+
reject(new Error(`Query ${querySerial !== null && querySerial !== void 0 ? querySerial : queryName} not found or you do not have access.`));
|
|
278
|
+
removeListener === null || removeListener === void 0 ? void 0 : removeListener();
|
|
279
|
+
}, QUERY_TIMEOUT);
|
|
280
|
+
function handleConfigAdded(event) {
|
|
281
|
+
const addedQueries = [event === null || event === void 0 ? void 0 : event.detail];
|
|
282
|
+
const matchingQuery = getMatchingQuery(addedQueries);
|
|
283
|
+
if (matchingQuery) {
|
|
284
|
+
resolve(matchingQuery);
|
|
285
|
+
clearTimeout(timeout);
|
|
286
|
+
removeListener === null || removeListener === void 0 ? void 0 : removeListener();
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
removeListener = client.addEventListener(dh.Client.EVENT_CONFIG_ADDED, handleConfigAdded);
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
if (querySerial == null && queryName == null) {
|
|
293
|
+
throw new InvalidMetadataError('Invalid query');
|
|
294
|
+
}
|
|
295
|
+
// Get the query from the known configs,
|
|
296
|
+
const matchingQuery = getMatchingQuery(client.getKnownConfigs());
|
|
297
|
+
if (matchingQuery) {
|
|
298
|
+
return matchingQuery;
|
|
299
|
+
}
|
|
300
|
+
if (queryName === WEB_CLIENT_DATA_CORE_QUERY) {
|
|
301
|
+
// WebClientData is a special case - try and wait for it to become available
|
|
302
|
+
return waitForAddedQuery();
|
|
303
|
+
}
|
|
304
|
+
throw new Error(`Query ${querySerial !== null && querySerial !== void 0 ? querySerial : queryName} not found or you do not have access.`);
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Creates PQ Query Object Promise
|
|
308
|
+
* @param params Function parameters
|
|
309
|
+
* @param params.client Enterprise client to use
|
|
310
|
+
* @param params.corePlusManager CorePlus manager for worker connections
|
|
311
|
+
* @param params.dh Deephaven Enterprise API
|
|
312
|
+
* @param params.queryName Query name to fetch
|
|
313
|
+
* @param params.querySerial Serial to check - if present, will use this instead of name to match
|
|
314
|
+
* @param params.objectDescriptor The descriptor of the object to fetch
|
|
315
|
+
* @param params.replicaSlot The replica slot to use, if null will use the designated worker
|
|
316
|
+
* @returns Resolves to the object fetched
|
|
317
|
+
*/
|
|
318
|
+
export async function makeQueryObjectPromise({ client, corePlusManager, dh, queryName, querySerial, objectDescriptor, replicaSlot, }) {
|
|
319
|
+
const query = await makeQueryPromise({
|
|
320
|
+
client,
|
|
321
|
+
dh,
|
|
322
|
+
queryName: queryName !== null && queryName !== void 0 ? queryName : undefined,
|
|
323
|
+
querySerial: querySerial !== null && querySerial !== void 0 ? querySerial : undefined,
|
|
324
|
+
});
|
|
325
|
+
const designated = getWorkerForSlot(query, replicaSlot);
|
|
326
|
+
if (designated == null || designated.status !== QueryStatus.running) {
|
|
327
|
+
throw new Error(`Query ${query.name} is not running.`);
|
|
328
|
+
}
|
|
329
|
+
if (corePlusManager.isCommunityWorkerKind(query.workerKind)) {
|
|
330
|
+
const { grpcUrl, envoyPrefix, jsApiUrl } = designated;
|
|
331
|
+
const api = await corePlusManager.getApi(query.workerKind, jsApiUrl);
|
|
332
|
+
const coreConnection = await corePlusManager.getConnection(api, grpcUrl, envoyPrefix);
|
|
333
|
+
// DH-20545: We shouldn't pass the object definition we got from the Enterprise QueryInfo object directly to Core connection.getObject, as they may be incompatible.
|
|
334
|
+
return coreConnection.getObject({
|
|
335
|
+
name: objectDescriptor.name,
|
|
336
|
+
type: objectDescriptor.type,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
return designated.getObject(objectDescriptor);
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Creates PQ Table Promise
|
|
343
|
+
* Should use makeQueryObjectPromise when possible instead.
|
|
344
|
+
* @param params Function parameters
|
|
345
|
+
* @param params.client Enterprise client
|
|
346
|
+
* @param params.corePlusManager CorePlus manager for worker connections
|
|
347
|
+
* @param params.dh Deephaven Enterprise API
|
|
348
|
+
* @param params.queryName Query name
|
|
349
|
+
* @param params.querySerial Query serial
|
|
350
|
+
* @param params.tableName Table name
|
|
351
|
+
* @returns A promise for the table or tree table
|
|
352
|
+
*/
|
|
353
|
+
export async function makeQueryTablePromise({ client, corePlusManager, dh, queryName, querySerial, tableName, }) {
|
|
354
|
+
var _a;
|
|
355
|
+
const query = await makeQueryPromise({
|
|
356
|
+
client,
|
|
357
|
+
dh,
|
|
358
|
+
queryName,
|
|
359
|
+
querySerial: querySerial !== null && querySerial !== void 0 ? querySerial : undefined,
|
|
360
|
+
});
|
|
361
|
+
if (query.designated == null ||
|
|
362
|
+
query.designated.status !== QueryStatus.running) {
|
|
363
|
+
throw new Error(`Query ${queryName} is not running.`);
|
|
364
|
+
}
|
|
365
|
+
const isTreeTable = ((_a = query.designated.objects.find(({ name }) => name === tableName)) === null || _a === void 0 ? void 0 : _a.type) ===
|
|
366
|
+
dh.VariableType.TREETABLE;
|
|
367
|
+
const { designated } = query;
|
|
368
|
+
if (corePlusManager.isCommunityWorkerKind(query.workerKind)) {
|
|
369
|
+
const { grpcUrl, envoyPrefix, jsApiUrl } = designated;
|
|
370
|
+
const api = await corePlusManager.getApi(query.workerKind, jsApiUrl);
|
|
371
|
+
const coreConnection = await corePlusManager.getConnection(api, grpcUrl, envoyPrefix);
|
|
372
|
+
return coreConnection.getObject({
|
|
373
|
+
type: isTreeTable ? dh.VariableType.TREETABLE : dh.VariableType.TABLE,
|
|
374
|
+
name: tableName,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
return isTreeTable
|
|
378
|
+
? designated.getTreeTable(tableName)
|
|
379
|
+
: designated.getTable(tableName);
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Retrieves a table with the revision history for a given query.
|
|
383
|
+
* @param params Function parameters
|
|
384
|
+
* @param params.client Enterprise client
|
|
385
|
+
* @param params.corePlusManager CorePlus manager for worker connections
|
|
386
|
+
* @param params.dh Deephaven Enterprise API
|
|
387
|
+
* @param params.serial Serial of the query to get the revision history for.
|
|
388
|
+
* @returns A promise for the revision history table.
|
|
389
|
+
*/
|
|
390
|
+
export async function makeRevertTablePromise({ client, corePlusManager, dh, serial, }) {
|
|
391
|
+
const messageId = nanoid();
|
|
392
|
+
const message = JSON.stringify({
|
|
393
|
+
id: messageId,
|
|
394
|
+
serial,
|
|
395
|
+
});
|
|
396
|
+
const [, eventDetail] = await sendWebClientData2Message({
|
|
397
|
+
client,
|
|
398
|
+
corePlusManager,
|
|
399
|
+
dh,
|
|
400
|
+
messageId,
|
|
401
|
+
message,
|
|
402
|
+
type: WEB_CLIENT_REVERT_TABLE_TYPE,
|
|
403
|
+
name: WEB_CLIENT_REVERT_TABLE_NAME,
|
|
404
|
+
});
|
|
405
|
+
if (eventDetail.exportedObjects.length < 1) {
|
|
406
|
+
throw new Error('No objects exported from the widget');
|
|
407
|
+
}
|
|
408
|
+
const table = await eventDetail.exportedObjects[0].fetch();
|
|
409
|
+
return table;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Sends a message to a WebClientData2 widget and returns a promise that resolves to the event detail.
|
|
413
|
+
*
|
|
414
|
+
* @param params Function parameters
|
|
415
|
+
* @param params.client Enterprise client
|
|
416
|
+
* @param params.corePlusManager CorePlus manager for worker connections
|
|
417
|
+
* @param params.dh Deephaven Enterprise API
|
|
418
|
+
* @param params.messageId the ID of the message to send
|
|
419
|
+
* @param params.message the message to send
|
|
420
|
+
* @param params.type the type of the widget to send the message to
|
|
421
|
+
* @param params.name the name of the widget to send the message to
|
|
422
|
+
* @returns the event detail from the response
|
|
423
|
+
*/
|
|
424
|
+
export function sendWebClientData2Message({ client, corePlusManager, dh, messageId, message, type, name, }) {
|
|
425
|
+
return new Promise((resolve, reject) => {
|
|
426
|
+
// Handle message events from the workspace widget
|
|
427
|
+
const handleMessageEvent = (event, cleanupFn) => {
|
|
428
|
+
const eventDetail = JSON.parse(event.detail.getDataAsString());
|
|
429
|
+
// Only handle the message if the ID matches
|
|
430
|
+
if (eventDetail.id === messageId) {
|
|
431
|
+
// We're handling the message, so we can clean up
|
|
432
|
+
cleanupFn();
|
|
433
|
+
if (eventDetail.error != null) {
|
|
434
|
+
log.error(`Error returned from ${name}:`, eventDetail.error);
|
|
435
|
+
reject(new Error(eventDetail.error));
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
resolve([eventDetail, event.detail]);
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
sendWidgetMessage({
|
|
442
|
+
client,
|
|
443
|
+
corePlusManager,
|
|
444
|
+
dh,
|
|
445
|
+
message,
|
|
446
|
+
onEventCallback: handleMessageEvent,
|
|
447
|
+
queryName: WEB_CLIENT_DATA_CORE_QUERY,
|
|
448
|
+
querySerial: null,
|
|
449
|
+
objectDefinition: {
|
|
450
|
+
type,
|
|
451
|
+
name,
|
|
452
|
+
},
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Sends a message to a widget on a Core+ query. Will call the provided callback with the event.
|
|
458
|
+
* It is the responsibility of the calling code to determine if the message is correct and to clean up the event listener.
|
|
459
|
+
* This method will time out after a set period if the message is not received.
|
|
460
|
+
*
|
|
461
|
+
* TODO: DH-20345: There are a number of issues with this function not properly
|
|
462
|
+
* handling Promise rejections and timeouts. Since it returns `void`, there is
|
|
463
|
+
* no way for an upstream caller to handle errors or even know about them.
|
|
464
|
+
*
|
|
465
|
+
* @param params Function parameters
|
|
466
|
+
* @param params.client Enterprise client
|
|
467
|
+
* @param params.corePlusManager CorePlus manager for worker connections
|
|
468
|
+
* @param params.dh Deephaven Enterprise API
|
|
469
|
+
* @param params.message the message to send
|
|
470
|
+
* @param params.onEventCallback the callback to handle the event when the message is received
|
|
471
|
+
* @param params.queryName the name of the query to send the message to, null if using serial
|
|
472
|
+
* @param params.querySerial the serial of the query to send the message to, null if using name
|
|
473
|
+
* @param params.objectDefinition the definition of the widget to send the message to
|
|
474
|
+
* @param params.replicaSlot the replica slot to send the message to, if null will use the designated worker
|
|
475
|
+
*/
|
|
476
|
+
export function sendWidgetMessage({ client, corePlusManager, dh, message, onEventCallback, queryName, querySerial, objectDefinition, replicaSlot, }) {
|
|
477
|
+
makeQueryPromise({
|
|
478
|
+
client,
|
|
479
|
+
dh,
|
|
480
|
+
queryName: queryName !== null && queryName !== void 0 ? queryName : undefined,
|
|
481
|
+
querySerial: querySerial !== null && querySerial !== void 0 ? querySerial : undefined,
|
|
482
|
+
}).then(async (query) => {
|
|
483
|
+
const designated = getWorkerForSlot(query, replicaSlot);
|
|
484
|
+
if (designated == null || designated.status !== QueryStatus.running) {
|
|
485
|
+
throw new Error(`Query ${query.name} is not running.`);
|
|
486
|
+
}
|
|
487
|
+
// Sending a message to a widget requires a Core+ worker
|
|
488
|
+
if (!corePlusManager.isCommunityWorkerKind(query.workerKind)) {
|
|
489
|
+
throw new Error(`Query ${query.name} is not running on a Core+ worker.`);
|
|
490
|
+
}
|
|
491
|
+
const { grpcUrl, envoyPrefix, jsApiUrl } = designated;
|
|
492
|
+
const api = await corePlusManager.getApi(query.workerKind, jsApiUrl);
|
|
493
|
+
const coreConnection = await corePlusManager.getConnection(api, grpcUrl, envoyPrefix);
|
|
494
|
+
const widget = (await coreConnection.getObject(objectDefinition));
|
|
495
|
+
// Set a timeout for the message to be received
|
|
496
|
+
// TODO: DH-20345: This timeout doesn't properly cleanup or surface an error
|
|
497
|
+
// to the caller.
|
|
498
|
+
const timeoutId = setTimeout(() => {
|
|
499
|
+
// This logs a warning rather than throwing an error because the message may still be received later and to avoid breaking the UI
|
|
500
|
+
log.warn(`No response from the server after ${MESSAGE_TIMEOUT}ms for message: ${message}`);
|
|
501
|
+
}, MESSAGE_TIMEOUT);
|
|
502
|
+
// Messages may come out of order, so the calling code is responsible for determining if the message is correct and clean up
|
|
503
|
+
const removeListener = widget.addEventListener(api.Widget.EVENT_MESSAGE, event => onEventCallback(event, () => {
|
|
504
|
+
clearTimeout(timeoutId);
|
|
505
|
+
removeListener();
|
|
506
|
+
widget.close();
|
|
507
|
+
}));
|
|
508
|
+
widget.sendMessage(message);
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Starts a query and returns a promise that resolves when the query starts running.
|
|
513
|
+
*
|
|
514
|
+
* This function:
|
|
515
|
+
* - Creates the query on the server
|
|
516
|
+
* - Listens for status updates
|
|
517
|
+
* - Auto-starts temporary queries (which don't start automatically)
|
|
518
|
+
* - Resolves when the query reaches running status
|
|
519
|
+
* - Rejects if the query fails to start
|
|
520
|
+
*
|
|
521
|
+
* @param dh Deephaven Enterprise API
|
|
522
|
+
* @param client Enterprise client to create the query with
|
|
523
|
+
* @param draftQuery Draft query configuration to start
|
|
524
|
+
* @returns Promise that resolves to the QueryInfo when the query starts running
|
|
525
|
+
* @throws Error if the query fails to start or encounters an error
|
|
526
|
+
*/
|
|
527
|
+
export async function startQuery(dh, client, draftQuery) {
|
|
528
|
+
const serial = await client.createQuery(draftQuery);
|
|
529
|
+
const queryInfoPromise = new Promise((resolve, reject) => {
|
|
530
|
+
const removeEventListener = client.addEventListener(dh.Client.EVENT_CONFIG_UPDATED, ({ detail }) => {
|
|
531
|
+
var _a, _b, _c, _d;
|
|
532
|
+
if (detail.serial !== serial) {
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
if (((_a = detail.designated) === null || _a === void 0 ? void 0 : _a.status) === QueryStatus.running) {
|
|
536
|
+
removeEventListener();
|
|
537
|
+
resolve(detail);
|
|
538
|
+
}
|
|
539
|
+
if (((_b = detail.designated) === null || _b === void 0 ? void 0 : _b.status) === QueryStatus.error ||
|
|
540
|
+
((_c = detail.designated) === null || _c === void 0 ? void 0 : _c.status) === QueryStatus.failed) {
|
|
541
|
+
removeEventListener();
|
|
542
|
+
reject(new Error('Query failed to start', {
|
|
543
|
+
cause: (_d = detail.designated) === null || _d === void 0 ? void 0 : _d.shortCauses,
|
|
544
|
+
}));
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
});
|
|
548
|
+
// Temporary queries don't auto start, so we need to start it manually.
|
|
549
|
+
if (draftQuery.scheduler.type === QueryScheduler.TYPES.TEMPORARY) {
|
|
550
|
+
client.restartQueries([serial]);
|
|
551
|
+
}
|
|
552
|
+
return queryInfoPromise;
|
|
553
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
export * from './CorePlusManager';
|
|
1
2
|
export * from './DraftQuery';
|
|
2
3
|
export * from './QueryColumns';
|
|
3
4
|
export * from './QueryDisplayType';
|
|
4
5
|
export * from './QueryScheduler';
|
|
5
6
|
export * from './QuerySchedulerValidation';
|
|
7
|
+
export * from './QueryStatus';
|
|
6
8
|
export * from './QueryType';
|
|
9
|
+
export * from './QueryUtils';
|
|
7
10
|
export * from './WorkerThreadUtils';
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
export * from './CorePlusManager';
|
|
1
2
|
export * from './DraftQuery';
|
|
2
3
|
export * from './QueryColumns';
|
|
3
4
|
export * from './QueryDisplayType';
|
|
4
5
|
export * from './QueryScheduler';
|
|
5
6
|
export * from './QuerySchedulerValidation';
|
|
7
|
+
export * from './QueryStatus';
|
|
6
8
|
export * from './QueryType';
|
|
9
|
+
export * from './QueryUtils';
|
|
7
10
|
export * from './WorkerThreadUtils';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deephaven-enterprise/query-utils",
|
|
3
|
-
"version": "2026.1.
|
|
3
|
+
"version": "2026.1.28--",
|
|
4
4
|
"description": "Deephaven Enterprise Query Utils",
|
|
5
5
|
"author": "Deephaven Data Labs LLC",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@deephaven/jsapi-types": "^1.0.0-dev0.36.1",
|
|
21
|
+
"@deephaven-enterprise/jsapi-types": "file:../jsapi-types",
|
|
21
22
|
"@deephaven/log": "^0.97.0",
|
|
22
23
|
"@deephaven/utils": "^0.97.0",
|
|
23
24
|
"@internationalized/date": "^3.5.5",
|