Skip to content

Commit 43f1963

Browse files
authored
feat: DH-21947: Split out fetchVariableDefinitionByPredicate function (#2634)
DH-21947: Split out fetchVariableDefinitionByPredicate function to support VS code extension
1 parent 70a3085 commit 43f1963

3 files changed

Lines changed: 117 additions & 7 deletions

File tree

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/jsapi-utils/src/ConnectionUtils.test.ts

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import dh from '@deephaven/jsapi-shim';
22
import { TimeoutError } from '@deephaven/utils';
3-
import { fetchVariableDefinition } from './ConnectionUtils';
3+
import {
4+
fetchVariableDefinition,
5+
fetchVariableDefinitionByPredicate,
6+
} from './ConnectionUtils';
47

58
const mockRemoveListener = jest.fn();
69
const mockSubscribeToFieldUpdates = jest.fn(
@@ -94,3 +97,81 @@ it('throws an Error if variable not found', async () => {
9497
await expect(fetchPromise).rejects.not.toThrow(TimeoutError);
9598
expect(mockRemoveListener).toHaveBeenCalled();
9699
});
100+
101+
describe('fetchVariableDefinitionByPredicate', () => {
102+
it('finds definition matching predicate', async () => {
103+
const predicate = (def: dh.ide.VariableDefinition) =>
104+
def.type === dh.VariableType.FIGURE;
105+
106+
const fetchPromise = fetchVariableDefinitionByPredicate(
107+
connection,
108+
predicate
109+
);
110+
111+
expect(mockSubscribeToFieldUpdates).toHaveBeenCalled();
112+
expect(mockRemoveListener).not.toHaveBeenCalled();
113+
114+
const listener = mockSubscribeToFieldUpdates.mock.calls[0][0];
115+
116+
listener({
117+
created: [testDefinition1, testDefinition2],
118+
updated: [],
119+
removed: [],
120+
});
121+
122+
const result = await fetchPromise;
123+
124+
expect(result).toBe(testDefinition2);
125+
expect(mockRemoveListener).toHaveBeenCalled();
126+
});
127+
128+
it('throws a TimeoutError if finding the variable timed out', async () => {
129+
const timeout = 1000;
130+
const predicate = (def: dh.ide.VariableDefinition) =>
131+
def.title.startsWith('NON_EXISTENT');
132+
133+
const fetchPromise = fetchVariableDefinitionByPredicate(
134+
connection,
135+
predicate,
136+
timeout,
137+
'Custom error message'
138+
);
139+
140+
expect(mockSubscribeToFieldUpdates).toHaveBeenCalled();
141+
expect(mockRemoveListener).not.toHaveBeenCalled();
142+
143+
jest.advanceTimersByTime(timeout + 2000);
144+
145+
await expect(fetchPromise).rejects.toThrow(TimeoutError);
146+
await expect(fetchPromise).rejects.toThrow('Timeout: Custom error message');
147+
expect(mockRemoveListener).toHaveBeenCalled();
148+
});
149+
150+
it('throws an Error if no definition matches predicate', async () => {
151+
const predicate = (def: dh.ide.VariableDefinition) =>
152+
def.title === 'NON_EXISTENT';
153+
154+
const fetchPromise = fetchVariableDefinitionByPredicate(
155+
connection,
156+
predicate,
157+
undefined,
158+
'Custom not found message'
159+
);
160+
161+
expect(mockSubscribeToFieldUpdates).toHaveBeenCalled();
162+
expect(mockRemoveListener).not.toHaveBeenCalled();
163+
164+
const listener = mockSubscribeToFieldUpdates.mock.calls[0][0];
165+
166+
listener({
167+
created: [testDefinition1],
168+
updated: [],
169+
removed: [],
170+
});
171+
172+
await expect(fetchPromise).rejects.toThrow(Error);
173+
await expect(fetchPromise).rejects.toThrow('Custom not found message');
174+
await expect(fetchPromise).rejects.not.toThrow(TimeoutError);
175+
expect(mockRemoveListener).toHaveBeenCalled();
176+
});
177+
});

packages/jsapi-utils/src/ConnectionUtils.ts

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,37 +5,65 @@ import { TimeoutError } from '@deephaven/utils';
55
export const FETCH_TIMEOUT = 10_000;
66

77
/**
8-
* Fetch the definition for a variable given a connection. Subscribes to field updates and triggers when the variable is found.
8+
* Fetch the definition for a variable given a connection. Waits for the next
9+
* field update event and resolves if the variable is found in the created
10+
* variables.
911
* @param connection Connection to get the variable from
1012
* @param name Name of the definition to fetch
1113
* @param timeout Timeout for the fetch
12-
* @returns Promise the resolves to the variable definition if found, or rejects if there's an error or the timeout has exceeded
14+
* @returns Promise that resolves to the variable definition if found in the next field update,
15+
* or rejects if the variable is not found in that update or if the timeout is exceeded
1316
*/
1417
export function fetchVariableDefinition(
1518
connection: dh.IdeConnection,
1619
name: string,
1720
timeout = FETCH_TIMEOUT
21+
): Promise<dh.ide.VariableDefinition> {
22+
return fetchVariableDefinitionByPredicate(
23+
connection,
24+
def => def.title === name,
25+
timeout,
26+
`Variable ${name} not found`
27+
);
28+
}
29+
30+
/**
31+
* Fetch the definition for a variable given a connection. Waits for the next
32+
* field update event and resolves if a variable matching the predicate is found
33+
* in the created variables.
34+
* @param connection Connection to get the variable from
35+
* @param predicate Predicate function to test each variable definition
36+
* @param timeout Timeout for the fetch
37+
* @param errorMessage Optional error message for timeout and not found errors
38+
* @returns Promise that resolves to the variable definition if found in the next field update,
39+
* or rejects if no matching variable is found in that update or if the timeout is exceeded
40+
*/
41+
export function fetchVariableDefinitionByPredicate(
42+
connection: dh.IdeConnection,
43+
predicate: (definition: dh.ide.VariableDefinition) => boolean,
44+
timeout = FETCH_TIMEOUT,
45+
errorMessage = 'Variable not found'
1846
): Promise<dh.ide.VariableDefinition> {
1947
return new Promise<dh.ide.VariableDefinition>((resolve, reject) => {
2048
let removeListener: () => void;
2149

2250
const timeoutId = setTimeout(() => {
2351
removeListener?.();
24-
reject(new TimeoutError(`Timeout looking for variable ${name}`));
52+
reject(new TimeoutError(`Timeout: ${errorMessage}`));
2553
}, timeout);
2654

2755
/**
28-
* Checks if the variable we're looking for is in the changes, and resolves the promise if it does
56+
* Checks if a variable matching the predicate is in the changes, and resolves the promise if it does
2957
* @param changes Variables changes that have occurred
3058
*/
3159
function handleFieldUpdates(changes: dh.ide.VariableChanges): void {
32-
const definition = changes.created.find(def => def.title === name);
60+
const definition = changes.created.find(predicate);
3361
clearTimeout(timeoutId);
3462
removeListener?.();
3563
if (definition != null) {
3664
resolve(definition);
3765
} else {
38-
reject(new Error(`Variable ${name} not found`));
66+
reject(new Error(errorMessage));
3967
}
4068
}
4169

0 commit comments

Comments
 (0)