Skip to content

Commit

Permalink
fix(dynamic-plugins): handle plugin initialization failures (janus-id…
Browse files Browse the repository at this point in the history
  • Loading branch information
Hyperkid123 committed Nov 3, 2023
1 parent 8691904 commit 9b79fdc
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 21 deletions.
5 changes: 5 additions & 0 deletions .changeset/breezy-trainers-do.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'app': patch
---

Add extra error handling while initializing dynamic front-end plugins. If a plugin fails to initialize, it won't be registered and won't be rendered at the expected place.
42 changes: 24 additions & 18 deletions packages/app/src/components/DynamicRoot/DynamicRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import defaultAppComponents from './defaultAppComponents';
import bindAppRoutes from '../../utils/dynamicUI/bindAppRoutes';
import overrideBaseUrlConfigs from '../../utils/dynamicUI/overrideBaseUrlConfigs';
import useAsync from 'react-use/lib/useAsync';
import Loader from './Loader';

const DynamicRoot = ({
afterInit,
Expand Down Expand Up @@ -84,12 +85,19 @@ const DynamicRoot = ({
});
}

const providerMountPoints = mountPoints.map(
({ module, importName, mountPoint, scope }) => ({
mountPoint,
Component: remotePlugins[scope][module][importName],
}),
);
const providerMountPoints = mountPoints.reduce<
{ mountPoint: string; Component: React.ComponentType<{}> }[]
>((acc, { module, importName, mountPoint, scope }) => {
const Component = remotePlugins[scope]?.[module]?.[importName];
// Only add mount points that have a component
if (Component) {
acc.push({
mountPoint,
Component: remotePlugins[scope][module][importName],
});
}
return acc;
}, []);

const mountPointComponents = providerMountPoints.reduce<{
[mountPoint: string]: ScalprumMountPoint[];
Expand Down Expand Up @@ -123,12 +131,12 @@ const DynamicRoot = ({
}, [initialized, components, initializeRemoteModules]);

if (!initialized || !components) {
return null;
return <Loader />;
}

return (
<DynamicRootContext.Provider value={components}>
{ChildComponent ? <ChildComponent /> : <div>Loading</div>}
{ChildComponent ? <ChildComponent /> : <Loader />}
</DynamicRootContext.Provider>
);
};
Expand All @@ -141,7 +149,7 @@ const ScalprumRoot = ({
apis: AnyApiFactory[];
afterInit: () => Promise<{ default: React.ComponentType }>;
}) => {
const { loading, error, value } = useAsync(async () => {
const { loading, value } = useAsync(async () => {
const config = ConfigReader.fromConfigs(
overrideBaseUrlConfigs(await defaultConfigLoader()),
);
Expand All @@ -156,25 +164,23 @@ const ScalprumRoot = ({
};
});

if (loading || !value?.scalprumConfig) {
return null;
}

if (error) {
return <div>Error</div>;
if (loading) {
return <Loader />;
}

return (
<ScalprumProvider
config={value.scalprumConfig}
config={value?.scalprumConfig ?? {}}
pluginSDKOptions={{
pluginLoaderOptions: {
postProcessManifest: manifest => {
return {
...manifest,
loadScripts: manifest.loadScripts.map(
script =>
`${value.baseUrl}/api/scalprum/${manifest.name}/${script}`,
`${value?.baseUrl ?? ''}/api/scalprum/${
manifest.name
}/${script}`,
),
};
},
Expand All @@ -183,7 +189,7 @@ const ScalprumRoot = ({
>
<DynamicRoot
afterInit={afterInit}
scalprumConfig={value.scalprumConfig}
scalprumConfig={value?.scalprumConfig ?? {}}
apis={apis}
/>
</ScalprumProvider>
Expand Down
22 changes: 22 additions & 0 deletions packages/app/src/components/DynamicRoot/Loader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import CircularProgress from '@mui/material/CircularProgress';
import Box from '@mui/material/Box';

const Loader = () => {
return (
<Box
sx={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: 'calc(100vw - 16px)',
height: 'calc(100vh - 16px)',
backgroundColor: '#F8F8F8',
}}
>
<CircularProgress />
</Box>
);
};

export default Loader;
13 changes: 10 additions & 3 deletions packages/app/src/utils/dynamicUI/initializeRemotePlugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,32 @@ const initializeRemotePlugins = async (
scalprumConfig: AppsConfig,
requiredModules: { scope: string; module: string }[],
): Promise<RemotePlugins> => {
await Promise.all(
await Promise.allSettled(
requiredModules.map(({ scope, module }) =>
processManifest(scalprumConfig[scope].manifestLocation!, scope, module),
processManifest(scalprumConfig[scope]?.manifestLocation!, scope, module),
),
);
const remotePlugins = await Promise.all(
let remotePlugins = await Promise.all(
requiredModules.map(({ scope, module }) =>
pluginStore
.getExposedModule<{
[importName: string]: React.ComponentType<{}>;
}>(scope, module)
// silently fail if the module loading fails
.catch(() => undefined)
.then(remoteModule => ({
module,
scope,
remoteModule,
})),
),
);
// remove all remote modules that are undefined
remotePlugins = remotePlugins.filter(({ remoteModule }) =>
Boolean(remoteModule),
);
const scopedRegistry = remotePlugins.reduce<RemotePlugins>((acc, curr) => {
if (!curr.remoteModule) return acc;
if (!acc[curr.scope]) {
acc[curr.scope] = {};
}
Expand Down

0 comments on commit 9b79fdc

Please sign in to comment.