Skip to content

Commit

Permalink
feat: Add ObjectFetcher context and useObjectFetcher hook (#1753)
Browse files Browse the repository at this point in the history
- Register a context that will fetch an object given the provided object
metadata
- Pass in a `VariableDescriptor` to fetch an object
- On Enterprise, the `VariableDescriptor` object will be extended to
include connection information (such as which query/session the variable
is from)

BREAKING CHANGE: 
- `useConnection` is moved from `jsapi-components` package to
`app-utils` package
- Should only be used at the app level, as there could be multiple
connections
- `WidgetDefinition` has been renamed to `WidgetDescriptor`
  • Loading branch information
mofojed authored Feb 2, 2024
1 parent 9f1bff3 commit 2cd46ce
Show file tree
Hide file tree
Showing 34 changed files with 459 additions and 227 deletions.
26 changes: 21 additions & 5 deletions packages/app-utils/src/components/ConnectionBootstrap.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import React, { useEffect, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { LoadingOverlay } from '@deephaven/components';
import { useApi, useClient } from '@deephaven/jsapi-bootstrap';
import type { IdeConnection } from '@deephaven/jsapi-types';
import { ConnectionContext } from '@deephaven/jsapi-components';
import {
ObjectFetcherContext,
sanitizeVariableDescriptor,
useApi,
useClient,
} from '@deephaven/jsapi-bootstrap';
import type { IdeConnection, VariableDescriptor } from '@deephaven/jsapi-types';
import Log from '@deephaven/log';
import { assertNotNull } from '@deephaven/utils';
import ConnectionContext from './ConnectionContext';

const log = Log.module('@deephaven/app-utils.ConnectionBootstrap');

Expand Down Expand Up @@ -69,6 +75,14 @@ export function ConnectionBootstrap({
[api, connection]
);

const objectFetcher = useCallback(
async (descriptor: VariableDescriptor) => {
assertNotNull(connection, 'No connection available to fetch object with');
return connection.getObject(sanitizeVariableDescriptor(descriptor));
},
[connection]
);

if (connection == null || error != null) {
return (
<LoadingOverlay
Expand All @@ -81,7 +95,9 @@ export function ConnectionBootstrap({

return (
<ConnectionContext.Provider value={connection}>
{children}
<ObjectFetcherContext.Provider value={objectFetcher}>
{children}
</ObjectFetcherContext.Provider>
</ConnectionContext.Provider>
);
}
Expand Down
2 changes: 2 additions & 0 deletions packages/app-utils/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
export * from './AppBootstrap';
export * from './AuthBootstrap';
export * from './ConnectionBootstrap';
export * from './ConnectionContext';
export * from './FontBootstrap';
export * from './FontsLoaded';
export * from './PluginsBootstrap';
export * from './ThemeBootstrap';
export * from './useConnection';
export * from './useServerConfig';
export * from './useUser';
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import type { IdeConnection } from '@deephaven/jsapi-types';
import { useContextOrThrow } from '@deephaven/react-hooks';
import { ConnectionContext } from './ConnectionContext';

/**
* Retrieve the connection for the current context.
*
* @returns Connection for the current context
*/
export function useConnection(): IdeConnection {
return useContextOrThrow(
ConnectionContext,
Expand Down
42 changes: 17 additions & 25 deletions packages/code-studio/src/main/AppDashboards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import {
DehydratedDashboardPanelProps,
LazyDashboard,
} from '@deephaven/dashboard';
import { useConnection } from '@deephaven/jsapi-components';
import { VariableDefinition } from '@deephaven/jsapi-types';
import {
sanitizeVariableDescriptor,
useObjectFetcher,
} from '@deephaven/jsapi-bootstrap';
import LayoutManager, { ItemConfigType } from '@deephaven/golden-layout';
import { LoadingOverlay } from '@deephaven/components';
import EmptyDashboard from './EmptyDashboard';
Expand All @@ -30,36 +32,26 @@ export function AppDashboards({
plugins,
onAutoFillClick,
}: AppDashboardsProps): JSX.Element {
const connection = useConnection();
const fetchObject = useObjectFetcher();

const hydratePanel = useCallback(
(hydrateProps: DehydratedDashboardPanelProps, id: string) => {
const { metadata } = hydrateProps;
if (
metadata?.type != null &&
(metadata?.id != null || metadata?.name != null)
) {
// Looks like a widget, hydrate it as such
const widget: VariableDefinition =
metadata.id != null
? {
type: metadata.type,
id: metadata.id,
}
: {
type: metadata.type,
name: metadata.name,
title: metadata.name,
};
return {
fetch: async () => connection?.getObject(widget),
...hydrateProps,
localDashboardId: id,
};
try {
if (metadata != null) {
const widget = sanitizeVariableDescriptor(metadata);
return {
fetch: async () => fetchObject(widget),
...hydrateProps,
localDashboardId: id,
};
}
} catch (e: unknown) {
// Ignore being unable to get the variable descriptor, do the default dashboard hydration
}
return DashboardUtils.hydrate(hydrateProps, id);
},
[connection]
[fetchObject]
);

return (
Expand Down
3 changes: 1 addition & 2 deletions packages/code-studio/src/main/AppInit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
import { FileStorage } from '@deephaven/file-explorer';
import { useApi, useClient } from '@deephaven/jsapi-bootstrap';
import type { dh as DhType, IdeConnection } from '@deephaven/jsapi-types';
import { useConnection } from '@deephaven/jsapi-components';
import {
getSessionDetails,
loadSessionWrapper,
Expand Down Expand Up @@ -46,7 +45,7 @@ import {
WorkspaceSettings,
CustomizableWorkspace,
} from '@deephaven/redux';
import { useServerConfig, useUser } from '@deephaven/app-utils';
import { useConnection, useServerConfig, useUser } from '@deephaven/app-utils';
import { type PluginModuleMap, usePlugins } from '@deephaven/plugin';
import { setLayoutStorage as setLayoutStorageAction } from '../redux/actions';
import App from './App';
Expand Down
71 changes: 39 additions & 32 deletions packages/code-studio/src/main/AppMainContainer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
import React from 'react';
import { Provider } from 'react-redux';
import { render, screen } from '@testing-library/react';
import { ConnectionContext } from '@deephaven/app-utils';
import { ToolType } from '@deephaven/dashboard-core-plugins';
import { ApiContext } from '@deephaven/jsapi-bootstrap';
import { ConnectionContext } from '@deephaven/jsapi-components';
import {
ApiContext,
ObjectFetcher,
ObjectFetcherContext,
} from '@deephaven/jsapi-bootstrap';
import dh from '@deephaven/jsapi-shim';
import type {
IdeConnection,
Expand Down Expand Up @@ -70,36 +74,39 @@ function renderAppMainContainer({
sessionConfig = makeSessionConfig(),
match = makeMatch(),
plugins = new Map(),
objectFetcher = jest.fn() as ObjectFetcher,
} = {}) {
const store = createMockStore();
return render(
<Provider store={store}>
<ApiContext.Provider value={dh}>
<ConnectionContext.Provider value={connection}>
<AppMainContainer
dashboardData={dashboardData}
allDashboardData={dashboardData}
layoutStorage={layoutStorage as LayoutStorage}
saveWorkspace={saveWorkspace}
updateDashboardData={updateDashboardData}
updateWorkspaceData={updateWorkspaceData}
user={user}
workspace={workspace as Workspace}
workspaceStorage={workspaceStorage}
activeTool={activeTool}
setActiveTool={setActiveTool}
setDashboardIsolatedLinkerPanelId={
setDashboardIsolatedLinkerPanelId
}
client={client}
serverConfigValues={serverConfigValues}
dashboardOpenedPanelMaps={dashboardOpenedPanelMaps}
connection={connection}
session={session as unknown as IdeSession}
sessionConfig={sessionConfig}
match={match}
plugins={plugins}
/>
<ObjectFetcherContext.Provider value={objectFetcher}>
<AppMainContainer
dashboardData={dashboardData}
allDashboardData={dashboardData}
layoutStorage={layoutStorage as LayoutStorage}
saveWorkspace={saveWorkspace}
updateDashboardData={updateDashboardData}
updateWorkspaceData={updateWorkspaceData}
user={user}
workspace={workspace as Workspace}
workspaceStorage={workspaceStorage}
activeTool={activeTool}
setActiveTool={setActiveTool}
setDashboardIsolatedLinkerPanelId={
setDashboardIsolatedLinkerPanelId
}
client={client}
serverConfigValues={serverConfigValues}
dashboardOpenedPanelMaps={dashboardOpenedPanelMaps}
connection={connection}
session={session as unknown as IdeSession}
sessionConfig={sessionConfig}
match={match}
plugins={plugins}
/>
</ObjectFetcherContext.Provider>
</ConnectionContext.Provider>
</ApiContext.Provider>
</Provider>
Expand Down Expand Up @@ -200,17 +207,17 @@ it('listens for widgets properly', async () => {
describe('hydrates widgets correctly', () => {
const localDashboardId = DEFAULT_DASHBOARD_ID;
let connection: IdeConnection;
let objectFetcher: ObjectFetcher;
beforeEach(() => {
connection = makeConnection();
objectFetcher = jest.fn();
});

it('hydrates empty props with defaults', () => {
mockProp = {};
mockId = localDashboardId;
renderAppMainContainer({ connection });
expect(
screen.getByText('{"metadata":{},"localDashboardId":"default"}')
).toBeTruthy();
expect(screen.getByText('{"localDashboardId":"default"}')).toBeTruthy();
});
it('does not try and add fetch when metadata does not have widget metadata', () => {
mockProp = { metadata: {} };
Expand All @@ -223,14 +230,14 @@ describe('hydrates widgets correctly', () => {
it('hydrates a widget properly', () => {
mockProp = { metadata: { type: 'TestType', name: 'TestName' } };
mockId = localDashboardId;
expect(connection.getObject).not.toHaveBeenCalled();
renderAppMainContainer({ connection });
expect(objectFetcher).not.toHaveBeenCalled();
renderAppMainContainer({ objectFetcher });

expect(
screen.getByText(
'{"metadata":{"type":"TestType","name":"TestName"},"localDashboardId":"default"}'
)
).toBeTruthy();
expect(connection.getObject).toHaveBeenCalled();
expect(objectFetcher).toHaveBeenCalled();
});
});
3 changes: 2 additions & 1 deletion packages/code-studio/src/main/AppMainContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import {
dhSquareFilled,
vsHome,
} from '@deephaven/icons';
import { getVariableDescriptor } from '@deephaven/jsapi-bootstrap';
import dh from '@deephaven/jsapi-shim';
import type {
IdeConnection,
Expand Down Expand Up @@ -723,7 +724,7 @@ export class AppMainContainer extends Component<
this.emitLayoutEvent(PanelEvent.OPEN, {
dragEvent,
fetch: async () => connection?.getObject(widget),
widget,
widget: getVariableDescriptor(widget),
});
}

Expand Down
Loading

0 comments on commit 2cd46ce

Please sign in to comment.