Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 3 additions & 4 deletions admin-ui/app/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Route, Routes, Navigate } from 'react-router-dom'
import { useSelector } from 'react-redux'

// ----------- Layout Imports ---------------
import { processRoutes, processRoutesSync } from 'Plugins/PluginMenuResolver'
import { processRoutes } from 'Plugins/PluginMenuResolver'

import GluuSuspenseLoader from 'Routes/Apps/Gluu/GluuSuspenseLoader'

Expand All @@ -22,9 +22,8 @@ export const RoutedContent = () => {
const routes = await processRoutes()
setPluginMenus(routes)
} catch (error) {
console.error('Failed to load plugins:', error)
// Fallback to sync loading
setPluginMenus(processRoutesSync())
console.error('Failed to load plugin routes:', error)
setPluginMenus([])
}
}

Expand Down
9 changes: 7 additions & 2 deletions admin-ui/config/webpack.config.client.dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ const webpackConfig: WebpackConfig & { devServer?: DevServerConfig } = {
path.resolve(__dirname, '../jans_config_api_orval'),
path.resolve(__dirname, '..'), // Include root directory for api-client.ts
],
exclude: /node_modules/,
exclude: /(node_modules|\.test\.(ts|tsx)$)/,
use: 'babel-loader',
},
{
Expand All @@ -186,9 +186,14 @@ const webpackConfig: WebpackConfig & { devServer?: DevServerConfig } = {
},
{
test: /\.js$/,
exclude: /node_modules/,
exclude: /(node_modules|\.test\.js$)/,
use: 'babel-loader',
},
{
test: /\.test\.(js|ts|tsx)$/,
include: [config.srcDir, config.pluginsDir],
use: 'ignore-loader',
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader', 'postcss-loader'],
Expand Down
32 changes: 2 additions & 30 deletions admin-ui/plugins/PluginMenuResolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export async function processMenus() {
const metadata = await import(
/* webpackChunkName: "plugin-[request]" */
/* webpackMode: "lazy" */
/* webpackExclude: /\.test\.(js|jsx|ts|tsx)$/ */
`./${pluginName}/plugin-metadata`
)
return metadata.default.menus || []
Expand Down Expand Up @@ -45,6 +46,7 @@ export async function processRoutes() {
const metadata = await import(
/* webpackChunkName: "plugin-[request]" */
/* webpackMode: "lazy" */
/* webpackExclude: /\.test\.(js|jsx|ts|tsx)$/ */
`./${pluginName}/plugin-metadata`
)
return metadata.default.routes || []
Expand All @@ -67,36 +69,6 @@ export async function processRoutes() {
return pluginRoutes
}

// Synchronous fallback for backward compatibility
export function processMenusSync() {
let pluginMenus = []
plugins
.map((item) => item.metadataFile)
.forEach((path) => {
try {
pluginMenus = pluginMenus.concat(require(`${path}`).default.menus || [])
} catch (error) {
console.warn(`Failed to load plugin menus: ${path}`, error)
}
})
pluginMenus = sortMenu(pluginMenus)
return pluginMenus
}

export function processRoutesSync() {
let pluginRoutes = []
plugins
.map((item) => item.metadataFile)
.forEach((path) => {
try {
pluginRoutes = pluginRoutes.concat(require(`${path}`).default.routes || [])
} catch (error) {
console.warn(`Failed to load plugin routes: ${path}`, error)
}
})
return pluginRoutes
}

const sortMenu = (menu) => {
menu = sortParentMenu(menu)
return menu
Expand Down
25 changes: 18 additions & 7 deletions admin-ui/plugins/PluginReducersResolver.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
import plugins from '../plugins.config.json'
import reducerRegistry from 'Redux/reducers/ReducerRegistry'

function process() {
async function process() {
const metadataFilePath = plugins.map((item) => item.metadataFile)
let pluginReducers = []
metadataFilePath.forEach(async (path) => {
pluginReducers = await [...pluginReducers, ...require(`${path}`).default.reducers]

pluginReducers.forEach((element) => {
reducerRegistry.register(element.name, element.reducer)
})
})
for (const path of metadataFilePath) {
const pluginName = path?.match(/\.\/([^/]+)\/plugin-metadata/)?.[1]
if (pluginName) {
const metadata = await import(
/* webpackChunkName: "plugin-[request]" */
/* webpackMode: "lazy" */
/* webpackExclude: /\.test\.(js|jsx|ts|tsx)$/ */
`./${pluginName}/plugin-metadata`
)
const reducers = metadata.default.reducers || []
pluginReducers = [...pluginReducers, ...reducers]

reducers.forEach((element) => {
reducerRegistry.register(element.name, element.reducer)
})
}
}
Comment on lines +4 to +24
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Return the reducer list.

process() now resolves to undefined, but the rest of the codebase still expects an array of plugin reducers. Even in this file we keep building pluginReducers, so this looks accidental. Add return pluginReducers at the end (after the loop) so callers aren’t broken.

 async function process() {
   const metadataFilePath = plugins.map((item) => item.metadataFile)
   let pluginReducers = []
 
   for (const path of metadataFilePath) {
     const pluginName = path?.match(/\.\/([^/]+)\/plugin-metadata/)?.[1]
     if (pluginName) {
       const metadata = await import(
         /* webpackChunkName: "plugin-[request]" */
         /* webpackMode: "lazy" */
         /* webpackExclude: /\.test\.(js|jsx|ts|tsx)$/ */
         `./${pluginName}/plugin-metadata`
       )
       const reducers = metadata.default.reducers || []
       pluginReducers = [...pluginReducers, ...reducers]
 
       reducers.forEach((element) => {
         reducerRegistry.register(element.name, element.reducer)
       })
     }
   }
+  return pluginReducers
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async function process() {
const metadataFilePath = plugins.map((item) => item.metadataFile)
let pluginReducers = []
metadataFilePath.forEach(async (path) => {
pluginReducers = await [...pluginReducers, ...require(`${path}`).default.reducers]
pluginReducers.forEach((element) => {
reducerRegistry.register(element.name, element.reducer)
})
})
for (const path of metadataFilePath) {
const pluginName = path?.match(/\.\/([^/]+)\/plugin-metadata/)?.[1]
if (pluginName) {
const metadata = await import(
/* webpackChunkName: "plugin-[request]" */
/* webpackMode: "lazy" */
/* webpackExclude: /\.test\.(js|jsx|ts|tsx)$/ */
`./${pluginName}/plugin-metadata`
)
const reducers = metadata.default.reducers || []
pluginReducers = [...pluginReducers, ...reducers]
reducers.forEach((element) => {
reducerRegistry.register(element.name, element.reducer)
})
}
}
async function process() {
const metadataFilePath = plugins.map((item) => item.metadataFile)
let pluginReducers = []
for (const path of metadataFilePath) {
const pluginName = path?.match(/\.\/([^/]+)\/plugin-metadata/)?.[1]
if (pluginName) {
const metadata = await import(
/* webpackChunkName: "plugin-[request]" */
/* webpackMode: "lazy" */
/* webpackExclude: /\.test\.(js|jsx|ts|tsx)$/ */
`./${pluginName}/plugin-metadata`
)
const reducers = metadata.default.reducers || []
pluginReducers = [...pluginReducers, ...reducers]
reducers.forEach((element) => {
reducerRegistry.register(element.name, element.reducer)
})
}
}
return pluginReducers
}
🤖 Prompt for AI Agents
In admin-ui/plugins/PluginReducersResolver.js around lines 4 to 24, the async
function process builds pluginReducers but never returns it, causing callers to
receive undefined; add a final statement returning pluginReducers after the
for-loop so the function resolves to the array of reducers that the rest of the
code expects.

Comment on lines +8 to +24
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Bring back the fallback import.

Same concern as in the sagas resolver: without importing item.metadataFile when the regex misses, reducers shipped in plugins with non-standard metadata paths stop registering. Please add the fallback import branch so behaviour stays consistent with the menu resolver.

🤖 Prompt for AI Agents
In admin-ui/plugins/PluginReducersResolver.js around lines 8 to 24, the code
only imports plugin metadata when the path matches the regex and skips importing
item.metadataFile for non-standard paths, causing reducers from plugins with
custom metadata locations to not register; add a fallback branch that imports
the original path (e.g., await import(item.metadataFile)) when pluginName is not
found by the regex, then extract metadata.default.reducers from that fallback
import, append them to pluginReducers and register each reducer with
reducerRegistry.register exactly as done for the regex-success path so behavior
matches the menu resolver.

}
export default process
30 changes: 24 additions & 6 deletions admin-ui/plugins/PluginSagasResolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,31 @@ import plugins from '../plugins.config.json'

//get all metadata path

function process() {
async function process() {
let pluginSagas = []
plugins
.map((item) => item.metadataFile)
.forEach((path) => {
pluginSagas = [...pluginSagas, ...require(`${path}`).default.sagas]
})

const pluginPromises = plugins.map(async (item) => {
const path = item.metadataFile
const pluginName = path?.match(/\.\/([^/]+)\/plugin-metadata/)?.[1]
if (pluginName) {
const metadata = await import(
/* webpackChunkName: "plugin-[request]" */
/* webpackMode: "lazy" */
/* webpackExclude: /\.test\.(js|jsx|ts|tsx)$/ */
`./${pluginName}/plugin-metadata`
)
return metadata.default.sagas || []
}
return []
})
Comment on lines +8 to +21
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Restore the metadata fallback.

Previously we supported plugin entries whose metadataFile didn’t follow ./<plugin>/plugin-metadata by importing item.metadataFile directly (see the menu resolver’s fallback). Dropping that path here means those plugins now return [] and their sagas never load. Please reintroduce the fallback import (ideally inside a try/catch) so non-standard metadataFile paths keep working.

🤖 Prompt for AI Agents
In admin-ui/plugins/PluginSagasResolver.js around lines 8 to 21, the code only
imports metadata when metadataFile matches "./<plugin>/plugin-metadata" and
returns [] for all other paths; reintroduce the previous fallback by wrapping
the import in a try/catch: first attempt the existing dynamic import using the
extracted pluginName, and if that path isn't present or pluginName is falsy, try
await import(item.metadataFile) (or if the first import throws, fall back to
importing item.metadataFile inside the catch), then return
metadata.default.sagas || []; on any import error return [] (and optionally log
or ignore the error) so non-standard metadataFile paths still load their sagas.


const results = await Promise.allSettled(pluginPromises)
results.forEach((result) => {
if (result.status === 'fulfilled') {
pluginSagas = [...pluginSagas, ...result.value]
}
})

return pluginSagas
}
export default process
Loading