Skip to content

Commit

Permalink
feat: Display workerName and processInfoId in the console status bar (#…
Browse files Browse the repository at this point in the history
…1173)

Get optional workerName and processInfoId along with the login options
response from the DHE host, display in the console status bar.
  • Loading branch information
vbabich authored Apr 11, 2023
1 parent 5142a4d commit 85ce600
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 35 deletions.
11 changes: 8 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 14 additions & 4 deletions packages/code-studio/src/main/AppInit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
setDashboardData as setDashboardDataAction,
} from '@deephaven/dashboard';
import {
SessionDetails,
SessionWrapper,
setDashboardConnection as setDashboardConnectionAction,
setDashboardSessionWrapper as setDashboardSessionWrapperAction,
Expand Down Expand Up @@ -52,6 +53,7 @@ import {
createSessionWrapper,
getAuthType,
getLoginOptions,
getSessionDetails,
} from './SessionUtils';
import { PluginUtils } from '../plugins';
import LayoutStorage from '../storage/LayoutStorage';
Expand Down Expand Up @@ -98,11 +100,12 @@ async function loadPlugins(): Promise<DeephavenPluginModuleMap> {
}

async function loadSessionWrapper(
connection: IdeConnection
connection: IdeConnection,
sessionDetails: SessionDetails
): Promise<SessionWrapper | undefined> {
let sessionWrapper: SessionWrapper | undefined;
try {
sessionWrapper = await createSessionWrapper(connection);
sessionWrapper = await createSessionWrapper(connection, sessionDetails);
} catch (e) {
// Consoles may be disabled on the server, but we should still be able to start up and open existing objects
if (!isNoConsolesError(e)) {
Expand Down Expand Up @@ -170,7 +173,11 @@ function AppInit(props: AppInitProps) {
const coreClient = createCoreClient();
const authType = getAuthType();
log.info(`Login using auth type ${authType}...`);
await coreClient.login(await getLoginOptions(authType));
const [loginOptions, sessionDetails] = await Promise.all([
getLoginOptions(authType),
getSessionDetails(authType),
]);
await coreClient.login(loginOptions);

const newPlugins = await loadPlugins();
const connection = await (authType === AUTH_TYPE.ANONYMOUS &&
Expand All @@ -185,7 +192,10 @@ function AppInit(props: AppInitProps) {
setDisconnectError(null);
});

const sessionWrapper = await loadSessionWrapper(connection);
const sessionWrapper = await loadSessionWrapper(
connection,
sessionDetails
);
const name = 'user';

const storageService = coreClient.getStorageService();
Expand Down
62 changes: 36 additions & 26 deletions packages/code-studio/src/main/SessionUtils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { SessionWrapper } from '@deephaven/dashboard-core-plugins';
import {
SessionDetails,
SessionWrapper,
} from '@deephaven/dashboard-core-plugins';
import dh, {
CoreClient,
IdeConnection,
LoginOptions,
} from '@deephaven/jsapi-shim';
import {
LOGIN_OPTIONS_REQUEST,
requestParentResponse,
SESSION_DETAILS_REQUEST,
} from '@deephaven/jsapi-utils';
import Log from '@deephaven/log';
import shortid from 'shortid';
import NoConsolesError from './NoConsolesError';
Expand Down Expand Up @@ -50,7 +58,8 @@ export function createConnection(): IdeConnection {
* @returns A session and config that is ready to use
*/
export async function createSessionWrapper(
connection: IdeConnection
connection: IdeConnection,
details: SessionDetails
): Promise<SessionWrapper> {
log.info('Getting console types...');

Expand All @@ -72,7 +81,12 @@ export async function createSessionWrapper(

log.info('Console session established', config);

return { session, config, connection };
return {
session,
config,
connection,
details,
};
}

export function createCoreClient(): CoreClient {
Expand All @@ -83,29 +97,12 @@ export function createCoreClient(): CoreClient {
return new dh.CoreClient(websocketUrl);
}

export async function requestParentLoginOptions(): Promise<LoginOptions> {
if (window.opener == null) {
throw new Error('window.opener is null, unable to send auth request.');
}
return new Promise(resolve => {
const listener = (
event: MessageEvent<{
message: string;
payload: LoginOptions;
}>
) => {
const { data } = event;
log.debug('Received message', data);
if (data?.message !== 'loginOptions') {
log.debug('Ignore received message', data);
return;
}
window.removeEventListener('message', listener);
resolve(data.payload);
};
window.addEventListener('message', listener);
window.opener.postMessage('requestLoginOptionsFromParent', '*');
});
async function requestParentLoginOptions(): Promise<LoginOptions> {
return requestParentResponse<LoginOptions>(LOGIN_OPTIONS_REQUEST);
}

async function requestParentSessionDetails(): Promise<SessionDetails> {
return requestParentResponse<SessionDetails>(SESSION_DETAILS_REQUEST);
}

export async function getLoginOptions(
Expand All @@ -121,4 +118,17 @@ export async function getLoginOptions(
}
}

export async function getSessionDetails(
authType: AUTH_TYPE
): Promise<SessionDetails> {
switch (authType) {
case AUTH_TYPE.PARENT:
return requestParentSessionDetails();
case AUTH_TYPE.ANONYMOUS:
return {};
default:
throw new Error(`Unknown auth type: ${authType}`);
}
}

export default { createSessionWrapper };
16 changes: 15 additions & 1 deletion packages/dashboard-core-plugins/src/panels/ConsolePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,8 @@ export class ConsolePanel extends PureComponent<
unzip,
} = this.props;
const { consoleSettings, error, objectMap } = this.state;
const { config, session, connection } = sessionWrapper;
const { config, session, connection, details = {} } = sessionWrapper;
const { workerName, processInfoId } = details;
const { id: sessionId, type: language } = config;

return (
Expand Down Expand Up @@ -349,6 +350,19 @@ export class ConsolePanel extends PureComponent<
<>
<div>&nbsp;</div>
<div>{ConsoleConstants.LANGUAGE_MAP.get(language)}</div>
{workerName != null && (
<>
<div>&nbsp;•&nbsp;</div>
{workerName}
</>
)}
{processInfoId != null && (
<>
<div>&nbsp;•&nbsp;</div>
{processInfoId}
<div>&nbsp;•</div>
</>
)}
<div>&nbsp;</div>
<div>
<HeapUsage
Expand Down
6 changes: 6 additions & 0 deletions packages/dashboard-core-plugins/src/redux/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,16 @@ export interface SessionConfig {
id: string;
}

export interface SessionDetails {
workerName?: string;
processInfoId?: string;
}

export interface SessionWrapper {
session: IdeSession;
connection: IdeConnection;
config: SessionConfig;
details?: SessionDetails;
}

/**
Expand Down
3 changes: 2 additions & 1 deletion packages/jsapi-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"@deephaven/filters": "file:../filters",
"@deephaven/jsapi-shim": "file:../jsapi-shim",
"@deephaven/log": "file:../log",
"@deephaven/utils": "file:../utils"
"@deephaven/utils": "file:../utils",
"shortid": "^2.2.16"
},
"devDependencies": {
"@deephaven/tsconfig": "file:../tsconfig"
Expand Down
82 changes: 82 additions & 0 deletions packages/jsapi-utils/src/MessageUtils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import {
makeMessage,
makeResponse,
Message,
requestParentResponse,
} from './MessageUtils';

it('Throws an exception if called on a window without parent', async () => {
await expect(requestParentResponse('request')).rejects.toThrow(
'window.opener is null, unable to send request.'
);
});

describe('requestParentResponse', () => {
let addListenerSpy: jest.SpyInstance;
let removeListenerSpy: jest.SpyInstance;
let listenerCallback;
let messageId;
const mockPostMessage = jest.fn((data: Message<unknown>) => {
messageId = data.id;
});
const originalWindowOpener = window.opener;
beforeEach(() => {
addListenerSpy = jest
.spyOn(window, 'addEventListener')
.mockImplementation((event, cb) => {
listenerCallback = cb;
});
removeListenerSpy = jest.spyOn(window, 'removeEventListener');
window.opener = { postMessage: mockPostMessage };
});
afterEach(() => {
addListenerSpy.mockRestore();
removeListenerSpy.mockRestore();
mockPostMessage.mockClear();
window.opener = originalWindowOpener;
messageId = undefined;
});

it('Posts message to parent and subscribes to response', async () => {
requestParentResponse('request');
expect(mockPostMessage).toHaveBeenCalledWith(
expect.objectContaining(makeMessage('request', messageId)),
'*'
);
expect(addListenerSpy).toHaveBeenCalledWith(
'message',
expect.any(Function)
);
});

it('Resolves with the payload from the parent window response and unsubscribes', async () => {
const PAYLOAD = 'PAYLOAD';
const promise = requestParentResponse('request');
listenerCallback({
data: makeResponse(messageId, PAYLOAD),
});
const result = await promise;
expect(result).toBe(PAYLOAD);
expect(removeListenerSpy).toHaveBeenCalledWith('message', listenerCallback);
});

it('Ignores unrelated response, rejects on timeout', async () => {
jest.useFakeTimers();
const promise = requestParentResponse('request');
listenerCallback({
data: makeMessage('wrong-id'),
});
jest.runOnlyPendingTimers();
await expect(promise).rejects.toThrow('Request timed out');
jest.useRealTimers();
});

it('Times out if no response', async () => {
jest.useFakeTimers();
const promise = requestParentResponse('request');
jest.runOnlyPendingTimers();
expect(removeListenerSpy).toHaveBeenCalled();
await expect(promise).rejects.toThrow('Request timed out');
jest.useRealTimers();
});
});
Loading

0 comments on commit 85ce600

Please sign in to comment.