Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Display workerName and processInfoId in the console status bar #1173

Merged
merged 7 commits into from
Apr 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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