Skip to content

Commit 5b2f23f

Browse files
authored
Fix an update issue caused by React being out of scope at a bad time (#1468)
Summary: Moving PixieAPIManager out of React's scope was a risky move. As it turns out, PixieAPIContext wasn't catching one of the updates during the embed authentication procedure because of this. By implementing an unholy hack to tell React when this happens, the `authorized` network call still fires with a bearer token. Type of change: /kind bugfix Test Plan: Try to embed Pixie using `embedPixieToken` to authenticate. Before, it would give up trying right before the `postMessage` comes through. After, it should try once more as soon as the auth token is provided to Pixie. --------- Signed-off-by: Nick Lanam <nlanam@pixielabs.ai>
1 parent 6ade5cb commit 5b2f23f

File tree

3 files changed

+28
-2
lines changed

3 files changed

+28
-2
lines changed

src/ui/src/api/api-context.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,50 @@ import * as React from 'react';
2121
import { ApolloProvider } from '@apollo/client/react';
2222

2323
import { AuthContext } from 'app/common/auth-context';
24+
import { SetStateFunc } from 'app/context/common';
2425
import { WithChildren } from 'app/utils/react-boilerplate';
2526

2627
import { PixieAPIClient, PixieAPIClientAbstract } from './api-client';
2728
import { PixieAPIManager } from './api-manager';
2829
import { PixieAPIClientOptions } from './api-options';
2930

30-
3131
export const PixieAPIContext = React.createContext<PixieAPIClientAbstract>(null);
3232
PixieAPIContext.displayName = 'PixieAPIContext';
3333

3434
export type PixieAPIContextProviderProps = WithChildren<PixieAPIClientOptions>;
3535

36+
declare global {
37+
interface Window {
38+
setApiContextUpdatesFromOutsideReact?: SetStateFunc<number>;
39+
}
40+
}
41+
3642
export const PixieAPIContextProvider: React.FC<PixieAPIContextProviderProps> = React.memo(({ children, ...opts }) => {
3743
const { authToken } = React.useContext(AuthContext);
3844

45+
// PixieAPIManager exists outside of React's scope. If it replaces its instance, we'll never know.
46+
// By putting a setState function somewhere that PixieAPIManager can reach, we can force the update from there.
47+
// This is not a typical or conventional thing to need to do, so please don't do this anywhere else.
48+
const [updateFromOutsideReact, setUpdateFromOutsideReact] = React.useState(0);
49+
React.useEffect(() => {
50+
// Technically this means PixieAPIContextProvider has to be singleton - and it already is, in practice.
51+
if (!window.setApiContextUpdatesFromOutsideReact) {
52+
window.setApiContextUpdatesFromOutsideReact = setUpdateFromOutsideReact;
53+
}
54+
return () => {
55+
delete window.setApiContextUpdatesFromOutsideReact;
56+
};
57+
}, []);
58+
React.useEffect(() => { /* Just need to update this context, nothing more */ }, [updateFromOutsideReact]);
59+
3960
// PixieAPIManager already reinitializes the API client when options change, making this context just a wrapper.
4061
React.useEffect(() => { PixieAPIManager.uri = opts.uri; }, [opts.uri]);
4162
React.useEffect(() => { PixieAPIManager.authToken = authToken; }, [authToken]);
4263
React.useEffect(() => { PixieAPIManager.onUnauthorized = opts.onUnauthorized; }, [opts.onUnauthorized]);
4364

4465
const instance = PixieAPIManager.instance as PixieAPIClient;
66+
const gqlClient = instance.getCloudClient().graphQL;
67+
React.useEffect(() => { /* Similar to the above trick, must re-render to update ApolloProvider */ }, [gqlClient]);
4568

4669
return !instance ? null : (
4770
<PixieAPIContext.Provider value={instance}>

src/ui/src/api/api-manager.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ export class PixieAPIManager {
4646
if (PixieAPIManager._authToken != null) opts.authToken = PixieAPIManager._authToken;
4747
if (PixieAPIManager._onUnauthorized != null) opts.onUnauthorized = PixieAPIManager._onUnauthorized;
4848
PixieAPIManager._instance = PixieAPIClient.create(opts);
49+
// See api-context.tsx for why this exists
50+
if (window.setApiContextUpdatesFromOutsideReact) window.setApiContextUpdatesFromOutsideReact((prev) => prev + 1);
4951
}
5052

5153
public static get uri() { return PixieAPIManager._uri; }

src/ui/src/containers/App/live.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,8 @@ export default function PixieWithContext(): React.ReactElement {
283283
if (errMsg) {
284284
// This is an error with pixie cloud, it is probably not relevant to the user.
285285
// Show a generic error message instead.
286-
showSnackbar({ message: 'There was a problem connecting to Pixie', autoHideDuration: 5000 });
286+
// Wait one update cycle, since this can happen in the middle of updating other components in some cases
287+
setTimeout(() => showSnackbar({ message: 'There was a problem connecting to Pixie', autoHideDuration: 5000 }));
287288
// eslint-disable-next-line no-console
288289
console.error(errMsg);
289290
}

0 commit comments

Comments
 (0)