Skip to content

Commit 09623f6

Browse files
committed
Split out fetchVariableDefinitionByPredicate function (#DH-21947)
1 parent 70a3085 commit 09623f6

3 files changed

Lines changed: 110 additions & 6 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: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,55 @@ export const FETCH_TIMEOUT = 10_000;
99
* @param connection Connection to get the variable from
1010
* @param name Name of the definition to fetch
1111
* @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
12+
* @returns Promise that resolves to the variable definition if found, or rejects if there's an error or the timeout has exceeded
1313
*/
1414
export function fetchVariableDefinition(
1515
connection: dh.IdeConnection,
1616
name: string,
1717
timeout = FETCH_TIMEOUT
18+
): Promise<dh.ide.VariableDefinition> {
19+
return fetchVariableDefinitionByPredicate(
20+
connection,
21+
def => def.title === name,
22+
timeout,
23+
`Variable ${name} not found`
24+
);
25+
}
26+
27+
/**
28+
* Fetch the definition for a variable given a connection. Subscribes to field updates and triggers when a variable matching the predicate is found.
29+
* @param connection Connection to get the variable from
30+
* @param predicate Predicate function to test each variable definition
31+
* @param timeout Timeout for the fetch
32+
* @param errorMessage Optional error message for timeout and not found errors
33+
* @returns Promise that resolves to the variable definition if found, or rejects if there's an error or the timeout has exceeded
34+
*/
35+
export function fetchVariableDefinitionByPredicate(
36+
connection: dh.IdeConnection,
37+
predicate: (definition: dh.ide.VariableDefinition) => boolean,
38+
timeout = FETCH_TIMEOUT,
39+
errorMessage = 'Variable not found'
1840
): Promise<dh.ide.VariableDefinition> {
1941
return new Promise<dh.ide.VariableDefinition>((resolve, reject) => {
2042
let removeListener: () => void;
2143

2244
const timeoutId = setTimeout(() => {
2345
removeListener?.();
24-
reject(new TimeoutError(`Timeout looking for variable ${name}`));
46+
reject(new TimeoutError(`Timeout: ${errorMessage}`));
2547
}, timeout);
2648

2749
/**
28-
* Checks if the variable we're looking for is in the changes, and resolves the promise if it does
50+
* Checks if a variable matching the predicate is in the changes, and resolves the promise if it does
2951
* @param changes Variables changes that have occurred
3052
*/
3153
function handleFieldUpdates(changes: dh.ide.VariableChanges): void {
32-
const definition = changes.created.find(def => def.title === name);
54+
const definition = changes.created.find(predicate);
3355
clearTimeout(timeoutId);
3456
removeListener?.();
3557
if (definition != null) {
3658
resolve(definition);
3759
} else {
38-
reject(new Error(`Variable ${name} not found`));
60+
reject(new Error(errorMessage));
3961
}
4062
}
4163

0 commit comments

Comments
 (0)