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: Logging out #1244

Merged
merged 23 commits into from
May 2, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
748f088
Rebase off the latest
mofojed Apr 27, 2023
997ded6
Add '@deephaven/*' packages to remote component resolution
mofojed Apr 27, 2023
1c3e9a0
Add some more unit tests
mofojed Apr 28, 2023
e254df0
Update the react-transition-group mock
mofojed May 1, 2023
3856df9
Remove `channel` from `RefreshTokenBootstrap`
mofojed May 1, 2023
24cddce
Default to unknown in MessageUtils
mofojed May 1, 2023
4e596be
Fix up comments in useContextOrThrow
mofojed May 1, 2023
d167f82
Clean up `getLoginOptions` types to not require a promise
mofojed May 1, 2023
58e2ffc
Clean up how we delete the window token
mofojed May 1, 2023
6a8fbec
Remove TODO, filed a ticket for it
mofojed May 1, 2023
fc7dfe3
Wrap parsing the refresh token with a try/catch
mofojed May 1, 2023
c2dcfe5
Use addEventListener in the BroadcastChannel
mofojed May 1, 2023
466ffc9
Add the logo to the auth plugins directly
mofojed May 1, 2023
d225cea
Fix up package-lock.json
mofojed May 1, 2023
7d84daa
Don't show user info for anonymous users
mofojed May 1, 2023
952c2ae
Fix up types after package-lock.json updates
mofojed May 1, 2023
a5bc3af
Show the logout button in Pre-shared key case still
mofojed May 1, 2023
585d109
Actually fix the package-lock.json
mofojed May 1, 2023
1f464da
Move react-transition-group out from root __mocks__ folder
mofojed May 1, 2023
7a92226
Move vite/client to vite-env.d.ts
mofojed May 1, 2023
ae439be
Add declarations.d.ts file to allow importing png
mofojed May 1, 2023
352a1cf
Fix has we're using requestParentResponse to only pass back unknown
mofojed May 1, 2023
9d018fd
Use the public URL logo instead
mofojed May 1, 2023
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
66 changes: 55 additions & 11 deletions package-lock.json

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

4 changes: 3 additions & 1 deletion packages/app-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@
"@deephaven/auth-plugins": "file:../auth-plugins",
"@deephaven/components": "file:../components",
"@deephaven/jsapi-bootstrap": "file:../jsapi-bootstrap",
"@deephaven/jsapi-components": "file:../jsapi-components",
"@deephaven/jsapi-types": "file:../jsapi-types",
"@deephaven/jsapi-utils": "file:../jsapi-utils",
"@deephaven/log": "file:../log",
"@deephaven/redux": "file:../redux",
"@deephaven/react-hooks": "file:../react-hooks",
"@paciolan/remote-component": "2.13.0",
"@paciolan/remote-module-loader": "^3.0.2",
"fira": "mozilla/fira#4.202"
Expand Down
69 changes: 41 additions & 28 deletions packages/app-utils/src/components/AppBootstrap.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { AUTH_HANDLER_TYPE_ANONYMOUS } from '@deephaven/auth-plugins';
import { ApiContext } from '@deephaven/jsapi-bootstrap';
import { BROADCAST_LOGIN_MESSAGE } from '@deephaven/jsapi-utils';
import {
CoreClient,
IdeConnection,
Expand All @@ -19,13 +20,42 @@ jest.mock('../plugins', () => ({
loadModulePlugins: jest.fn(() => mockPluginsPromise),
}));

const mockChannel = {
postMessage: jest.fn(),
};
jest.mock('@deephaven/jsapi-components', () => ({
...jest.requireActual('@deephaven/jsapi-components'),
RefreshTokenBootstrap: jest.fn(({ children }) => children),
useBroadcastChannel: jest.fn(() => mockChannel),
useBroadcastLoginListener: jest.fn(),
}));
mattrunyon marked this conversation as resolved.
Show resolved Hide resolved

const mockChildText = 'Mock Child';
const mockChild = <div>{mockChildText}</div>;

function expectMockChild() {
return expect(screen.queryByText(mockChildText));
}

function renderComponent(client: CoreClient) {
const api = TestUtils.createMockProxy<DhType>({
CoreClient: (jest
.fn()
.mockImplementation(() => client) as unknown) as CoreClient,
});
return render(
<ApiContext.Provider value={api}>
<AppBootstrap apiUrl={API_URL} pluginsUrl={PLUGINS_URL}>
{mockChild}
</AppBootstrap>
</ApiContext.Provider>
);
}

beforeEach(() => {
jest.clearAllMocks();
});

it('should throw if api has not been bootstrapped', () => {
expect(() =>
render(
Expand All @@ -49,19 +79,7 @@ it('should display an error if no login plugin matches the provided auth handler
getAuthConfigValues: mockGetAuthConfigValues,
login: mockLogin,
});
const api = TestUtils.createMockProxy<DhType>({
CoreClient: (jest
.fn()
.mockImplementation(() => client) as unknown) as CoreClient,
});

render(
<ApiContext.Provider value={api}>
<AppBootstrap apiUrl={API_URL} pluginsUrl={PLUGINS_URL}>
{mockChild}
</AppBootstrap>
</ApiContext.Provider>
);
renderComponent(client);
expectMockChild().toBeNull();
expect(mockGetAuthConfigValues).toHaveBeenCalled();

Expand Down Expand Up @@ -105,19 +123,8 @@ it('should log in automatically when the anonymous handler is supported', async
login: mockLogin,
getAsIdeConnection: mockGetAsConnection,
});
const api = TestUtils.createMockProxy<DhType>({
CoreClient: (jest
.fn()
.mockImplementation(() => client) as unknown) as CoreClient,
});

render(
<ApiContext.Provider value={api}>
<AppBootstrap apiUrl={API_URL} pluginsUrl={PLUGINS_URL}>
<div>{mockChild}</div>
</AppBootstrap>
</ApiContext.Provider>
);
renderComponent(client);

expectMockChild().toBeNull();
expect(mockLogin).not.toHaveBeenCalled();
Expand All @@ -127,25 +134,31 @@ it('should log in automatically when the anonymous handler is supported', async
await mockPluginsPromise;
});

expect(mockChannel.postMessage).not.toHaveBeenCalled();
expectMockChild().toBeNull();
expect(mockLogin).toHaveBeenCalled();
expect(screen.queryByTestId('auth-anonymous-loading')).not.toBeNull();
expect(screen.queryByTestId('auth-base-loading')).not.toBeNull();

// Wait for login to complete
await act(async () => {
mockLoginResolve();
});

expect(screen.queryByTestId('auth-anonymous-loading')).toBeNull();
expect(screen.queryByTestId('auth-base-loading')).toBeNull();
expect(screen.queryByTestId('connection-bootstrap-loading')).not.toBeNull();
expect(screen.queryByText(mockChildText)).toBeNull();
expect(mockChannel.postMessage).toHaveBeenCalledWith(
expect.objectContaining({
message: BROADCAST_LOGIN_MESSAGE,
})
);

// Wait for IdeConnection to resolve
await act(async () => {
mockConnectionResolve(mockConnection);
});

expect(screen.queryByTestId('auth-anonymous-loading')).toBeNull();
expect(screen.queryByTestId('auth-base-loading')).toBeNull();
expect(screen.queryByTestId('connection-bootstrap-loading')).toBeNull();
expectMockChild().not.toBeNull();
});
42 changes: 30 additions & 12 deletions packages/app-utils/src/components/AppBootstrap.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import React from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import '@deephaven/components/scss/BaseStyleSheet.scss';
import { ClientBootstrap } from '@deephaven/jsapi-bootstrap';
import {
RefreshTokenBootstrap,
useBroadcastLoginListener,
} from '@deephaven/jsapi-components';
import FontBootstrap from './FontBootstrap';
import ClientBootstrap from './ClientBootstrap';
import PluginsBootstrap from './PluginsBootstrap';
import AuthBootstrap from './AuthBootstrap';
import ConnectionBootstrap from './ConnectionBootstrap';
Expand Down Expand Up @@ -35,18 +39,32 @@ export function AppBootstrap({
children,
}: AppBootstrapProps) {
const serverUrl = getBaseUrl(apiUrl).origin;
const clientOptions = getConnectOptions();
const clientOptions = useMemo(() => getConnectOptions(), []);

// On logout, we reset the client and have user login again
const [logoutCount, setLogoutCount] = useState(0);
const onLogin = useCallback(() => undefined, []);
const onLogout = useCallback(() => {
setLogoutCount(value => value + 1);
}, []);
useBroadcastLoginListener(onLogin, onLogout);
return (
<FontBootstrap fontClassNames={fontClassNames}>
<ClientBootstrap serverUrl={serverUrl} options={clientOptions}>
<PluginsBootstrap pluginsUrl={pluginsUrl}>
<AuthBootstrap>
<ConnectionBootstrap>
<FontsLoaded>{children}</FontsLoaded>
</ConnectionBootstrap>
</AuthBootstrap>
</PluginsBootstrap>
</ClientBootstrap>
<PluginsBootstrap pluginsUrl={pluginsUrl}>
<ClientBootstrap
serverUrl={serverUrl}
options={clientOptions}
key={logoutCount}
>
<RefreshTokenBootstrap>
<AuthBootstrap>
<ConnectionBootstrap>
<FontsLoaded>{children}</FontsLoaded>
</ConnectionBootstrap>
</AuthBootstrap>
</RefreshTokenBootstrap>
</ClientBootstrap>
</PluginsBootstrap>
</FontBootstrap>
);
}
Expand Down
Loading