feat: integrate dynamic plugin infrastructure - #1109
Conversation
Add plugin loading support to the Modern WebUI using @freeipa/plugin-sdk. Plugins are discovered via a server manifest and loaded at runtime using dynamic import(), without requiring a rebuild of the host application. Changes: - Expose shared dependencies (React, PatternFly, Redux) for plugins - Load plugins asynchronously before initial render - Support dynamic route and navigation item registration from plugins - Support dynamic Redux reducer injection via store.replaceReducer() - Add Dashboard page with ExtensionSlot for plugin-contributed widgets Signed-off-by: Erik Belko <ebelko@redhat.com>
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- The async
bootstrapflow currently assumesloadPluginsandpluginRegistry.runPhase('ready')always succeed; consider wrapping these in a try/catch with logging and a fallback to rendering the core app so a plugin failure doesn’t prevent the UI from loading entirely. - In
injectPluginReducer, silently returning when a key already exists can hide plugin integration issues; consider at least logging a warning or throwing for duplicate reducer keys so plugin collisions are easier to diagnose.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The async `bootstrap` flow currently assumes `loadPlugins` and `pluginRegistry.runPhase('ready')` always succeed; consider wrapping these in a try/catch with logging and a fallback to rendering the core app so a plugin failure doesn’t prevent the UI from loading entirely.
- In `injectPluginReducer`, silently returning when a key already exists can hide plugin integration issues; consider at least logging a warning or throwing for duplicate reducer keys so plugin collisions are easier to diagnose.
## Individual Comments
### Comment 1
<location path="src/main.tsx" line_range="55-68" />
<code_context>
- </Provider>
- </React.StrictMode>
-);
+async function bootstrap() {
+ await loadPlugins();
+ await pluginRegistry.runPhase("ready");
+
+ root.render(
+ <React.StrictMode>
+ <Provider store={store}>
+ <BrowserRouter basename={URL_PREFIX}>
+ <App />
+ </BrowserRouter>
+ </Provider>
+ </React.StrictMode>
+ );
+}
+
+bootstrap();
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Consider adding basic error handling around the async bootstrap pipeline.
`loadPlugins()` and `pluginRegistry.runPhase("ready")` are awaited without any error handling, so a failure in any plugin will prevent the app from rendering and leave users on a blank page. Wrap these calls in a `try/catch` that logs the error and still renders either the core app without the failing plugin(s) or a dedicated error screen, so one bad plugin doesn’t break the whole UI.
```suggestion
async function bootstrap() {
try {
await loadPlugins();
await pluginRegistry.runPhase("ready");
root.render(
<React.StrictMode>
<Provider store={store}>
<BrowserRouter basename={URL_PREFIX}>
<App />
</BrowserRouter>
</Provider>
</React.StrictMode>
);
} catch (error) {
// Ensure a plugin failure doesn't leave the user on a blank screen
console.error("Failed to initialize plugins during bootstrap:", error);
root.render(
<React.StrictMode>
<Provider store={store}>
<BrowserRouter basename={URL_PREFIX}>
<div role="alert" style={{ padding: 24 }}>
<h1>We couldn’t load all extensions</h1>
<p>
Some parts of the application failed to start correctly. You can try
refreshing the page. If the problem persists, please contact support.
</p>
</div>
</BrowserRouter>
</Provider>
</React.StrictMode>
);
}
}
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| async function bootstrap() { | ||
| await loadPlugins(); | ||
| await pluginRegistry.runPhase("ready"); | ||
|
|
||
| root.render( | ||
| <React.StrictMode> | ||
| <Provider store={store}> | ||
| <BrowserRouter basename={URL_PREFIX}> | ||
| <App /> | ||
| </BrowserRouter> | ||
| </Provider> | ||
| </React.StrictMode> | ||
| ); | ||
| } |
There was a problem hiding this comment.
suggestion (bug_risk): Consider adding basic error handling around the async bootstrap pipeline.
loadPlugins() and pluginRegistry.runPhase("ready") are awaited without any error handling, so a failure in any plugin will prevent the app from rendering and leave users on a blank page. Wrap these calls in a try/catch that logs the error and still renders either the core app without the failing plugin(s) or a dedicated error screen, so one bad plugin doesn’t break the whole UI.
| async function bootstrap() { | |
| await loadPlugins(); | |
| await pluginRegistry.runPhase("ready"); | |
| root.render( | |
| <React.StrictMode> | |
| <Provider store={store}> | |
| <BrowserRouter basename={URL_PREFIX}> | |
| <App /> | |
| </BrowserRouter> | |
| </Provider> | |
| </React.StrictMode> | |
| ); | |
| } | |
| async function bootstrap() { | |
| try { | |
| await loadPlugins(); | |
| await pluginRegistry.runPhase("ready"); | |
| root.render( | |
| <React.StrictMode> | |
| <Provider store={store}> | |
| <BrowserRouter basename={URL_PREFIX}> | |
| <App /> | |
| </BrowserRouter> | |
| </Provider> | |
| </React.StrictMode> | |
| ); | |
| } catch (error) { | |
| // Ensure a plugin failure doesn't leave the user on a blank screen | |
| console.error("Failed to initialize plugins during bootstrap:", error); | |
| root.render( | |
| <React.StrictMode> | |
| <Provider store={store}> | |
| <BrowserRouter basename={URL_PREFIX}> | |
| <div role="alert" style={{ padding: 24 }}> | |
| <h1>We couldn’t load all extensions</h1> | |
| <p> | |
| Some parts of the application failed to start correctly. You can try | |
| refreshing the page. If the problem persists, please contact support. | |
| </p> | |
| </div> | |
| </BrowserRouter> | |
| </Provider> | |
| </React.StrictMode> | |
| ); | |
| } | |
| } |
Signed-off-by: Erik Belko <ebelko@redhat.com>
|
This PR has not received any attention in 60 days. |
|
Hi @b3lix - Do you plan to continue working on this PR? |
|
Closing, as duplicate of #1110, also is present in it's own repo. |
|
This PR was opened for review of PoC, infrastructure has its own repository. This can be reopened in future. |
Add plugin loading support to the Modern WebUI using @freeipa/plugin-sdk. Plugins are discovered via a server manifest and loaded at runtime using dynamic import(), without requiring a rebuild of the host application. Changes:
Summary by Sourcery
Integrate a dynamic plugin infrastructure into the Modern WebUI and introduce a dashboard landing page that exposes extension points for plugin-provided content.
New Features:
Enhancements:
Build: