Skip to content

load scalprum - #998

Closed
MariaAga wants to merge 1 commit into
theforeman:developfrom
MariaAga:import-scalprum
Closed

load scalprum#998
MariaAga wants to merge 1 commit into
theforeman:developfrom
MariaAga:import-scalprum

Conversation

@MariaAga

@MariaAga MariaAga commented Jun 6, 2025

Copy link
Copy Markdown
Member

To setup:

  1. Create /etc/httpd/conf.d/05-foreman-ssl.d/consoledot.conf
    with ProxyPass /scalprum http://HOSTNAME:8001 (change HOSTNAME)

  2. run sudo systemctl restart httpd

  3. and run podman run -p 8001:8000 quay.io/redhat-services-prod/rh-platform-experien-tenant/landing-page-frontend:latest

  4. Also needs Fixes #37882 - Remove @theforeman/vendor foreman#10342

This pr deletes the use of frontend components as they clash with scalprum and should be replaces in #973

Summary by Sourcery

Enable Scalprum-based module federation by introducing a Scalprum context and provider wrapper for dynamic plugin loading, and adding the necessary Scalprum dependencies.

New Features:

  • Add ScalprumContext and ScalprumContextWrapper components for managing runtime plugin configuration and loading.

Enhancements:

  • Add "@scalprum/react-core" and "@scalprum/core" to project dependencies.
  • Configure plugin SDK options to transform plugin manifests based on CDN paths.
  • Provide a mock user API for ScalprumProvider authentication integration.

@sourcery-ai

sourcery-ai Bot commented Jun 6, 2025

Copy link
Copy Markdown

Reviewer's Guide

Integrate Scalprum dynamic plugin loading into the inventory UI by introducing a context wrapper with ScalprumProvider and adding the required Scalprum dependencies.

Class diagram for ScalprumContext and ScalprumContextWrapper integration

classDiagram
  class ScalprumContextWrapper {
    +config: object
    +setConfig(newConfig)
    +mockUser: object
    +render()
  }
  class ScalprumContext {
    <<context>>
  }
  ScalprumContextWrapper --> ScalprumContext : provides
  ScalprumContextWrapper --> ScalprumProvider : wraps
  ScalprumProvider <|-- ScalprumContextWrapper : conditional render
Loading

Class diagram for ScalprumProvider pluginSDKOptions and API mock

classDiagram
  class ScalprumProvider {
    +pluginSDKOptions: object
    +api: object
    +config: object
    +children: ReactNode
  }
  class pluginSDKOptions {
    +pluginLoaderOptions: object
  }
  class pluginLoaderOptions {
    +transformPluginManifest(manifest)
  }
  class api {
    +chrome: object
  }
  class chrome {
    +isBeta()
    +on()
    +auth: object
  }
  class auth {
    +getUser()
  }
  ScalprumProvider --> pluginSDKOptions
  pluginSDKOptions --> pluginLoaderOptions
  ScalprumProvider --> api
  api --> chrome
  chrome --> auth
Loading

File-Level Changes

Change Details Files
Add Scalprum context wrapper and provider for dynamic module loading
  • Create ScalprumContext with config state and setConfig callback
  • Implement ScalprumContextWrapper to wrap children with ScalprumProvider when config is set
  • Configure pluginSDKOptions to transform plugin manifest based on cdnPath in config
  • Provide a mock chrome.auth.getUser implementation for plugin authentication
webpack/common/ScalprumModule/ScalprumContext.js
Introduce Scalprum dependencies for plugin integration
  • Add @scalprum/react-core to project dependencies
  • Add @scalprum/core to project dependencies
package.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@MariaAga
MariaAga marked this pull request as draft June 6, 2025 15:16

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey @MariaAga - I've reviewed your changes and found some issues that need to be addressed.

Blocking issues:

  • Invalid JSX comment syntax (link)
Here's what I looked at during the review
  • 🔴 General issues: 1 blocking issue, 4 other issues
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread webpack/ForemanInventoryUpload/ForemanInventoryUpload.js Outdated
Comment thread webpack/ForemanInventoryUpload/ForemanInventoryUpload.js Outdated
Comment thread webpack/InsightsCloudSync/Components/InsightsTable/InsightsTableConstants.js Outdated
Comment thread webpack/common/ScalprumModule/ScalprumContext.js
Comment thread webpack/common/ScalprumModule/ScalprumContext.js Outdated
Comment thread webpack/common/ScalprumModule/ScalprumContext.js
Comment thread webpack/common/ScalprumModule/ScalprumContext.js Outdated
Comment thread webpack/InsightsCloudSync/Components/InsightsTable/InsightsTableConstants.js Outdated
@ShimShtein ShimShtein self-assigned this Jun 22, 2025

@jeremylenz jeremylenz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@MariaAga Can you please remove the changes to ForemanInventoryUpload from this pr? Then we can merge it, we've tested and it's working well. And then we can add the scalprum-wrapped components in the correct places.

@MariaAga
MariaAga marked this pull request as ready for review July 1, 2025 09:36

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey @MariaAga - I've reviewed your changes - here's some feedback:

  • Add a loading fallback (e.g., React.Suspense or an error boundary) around the ScalprumProvider/ScalprumComponent to handle slow or failed plugin loads gracefully.
  • Extract the mockUser stub out of the ScalprumContextWrapper into a dedicated fixture or util and swap in the real identity provider before production rollout.
  • Consider externalizing the manifestLocation and cdnPath construction (instead of hard-coding window.location.origin) to make the plugin host paths configurable per environment.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Add a loading fallback (e.g., React.Suspense or an error boundary) around the ScalprumProvider/ScalprumComponent to handle slow or failed plugin loads gracefully.
- Extract the mockUser stub out of the ScalprumContextWrapper into a dedicated fixture or util and swap in the real identity provider before production rollout.
- Consider externalizing the manifestLocation and cdnPath construction (instead of hard-coding window.location.origin) to make the plugin host paths configurable per environment.

## Individual Comments

### Comment 1
<location> `webpack/common/ScalprumModule/ScalprumContext.js:39` </location>
<code_context>
+    ),
+  };
+
+  const mockUser = {
+    entitlements: {},
+    identity: {
+      account_number: 'string',
+      org_id: 'string',
+      internal: {
+        org_id: 'string',
+        account_id: 'string',
+      },
+      type: 'string',
+      user: {
</code_context>

<issue_to_address>
Hardcoded mockUser may cause confusion or issues in production.

If this is for development only, clearly indicate that or allow it to be overridden in production.
</issue_to_address>

### Comment 2
<location> `webpack/common/ScalprumModule/ScalprumContext.js:88` </location>
<code_context>
+          api={{
+            chrome: {
+              isBeta: () => false,
+              on: () => {},
+              auth: {
+                getUser: () => Promise.resolve(mockUser),
</code_context>

<issue_to_address>
The chrome.on method is a no-op, which may break consumers expecting event handling.

Returning a no-op here may cause silent failures for consumers relying on event subscriptions. Suggest logging a warning or implementing a minimal event emitter.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
          api={{
            chrome: {
              isBeta: () => false,
              on: () => {},
              auth: {
                getUser: () => Promise.resolve(mockUser),
              },
            },
          }}
=======
          api={{
            chrome: (() => {
              // Minimal event emitter for mock chrome.on
              const listeners = {};
              return {
                isBeta: () => false,
                on: (event, callback) => {
                  if (!listeners[event]) {
                    listeners[event] = [];
                  }
                  listeners[event].push(callback);
                  // Log a warning for visibility in tests/mocks
                  // eslint-disable-next-line no-console
                  console.warn(`[Mock chrome.on] Subscribed to event: ${event}`);
                  // Return unsubscribe function for compatibility
                  return () => {
                    listeners[event] = listeners[event].filter(cb => cb !== callback);
                  };
                },
                // Optionally, provide a way to emit events in tests
                __emit: (event, ...args) => {
                  if (listeners[event]) {
                    listeners[event].forEach(cb => {
                      try {
                        cb(...args);
                      } catch (e) {
                        // eslint-disable-next-line no-console
                        console.error(`[Mock chrome.on] Error in event handler for ${event}:`, e);
                      }
                    });
                  }
                  // eslint-disable-next-line no-console
                  console.warn(`[Mock chrome.on] Emitted event: ${event}`, ...args);
                },
                auth: {
                  getUser: () => Promise.resolve(mockUser),
                },
              };
            })(),
          }}
>>>>>>> REPLACE

</suggested_fix>

### Comment 3
<location> `webpack/common/ScalprumModule/ScalprumContext.js:67` </location>
<code_context>
+        <ScalprumProvider
+          pluginSDKOptions={{
+            pluginLoaderOptions: {
+              transformPluginManifest: manifest => {
+                if (
+                  manifest.baseURL === 'auto' &&
+                  config[manifest.name]?.cdnPath
+                ) {
+                  const _cdnPath = config[manifest.name]?.cdnPath;
+                  return {
+                    ...manifest,
+                    baseURL: _cdnPath,
+                    loadScripts: manifest.loadScripts.map(
+                      script => `${_cdnPath}${script}`
+                    ),
+                  };
+                }
+                return manifest;
+              },
+            },
</code_context>

<issue_to_address>
transformPluginManifest mutates loadScripts URLs but assumes all scripts are relative.

This logic may fail if loadScripts includes absolute URLs. Please ensure only relative paths are prefixed, or clearly document the assumption that all scripts are relative.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
                    loadScripts: manifest.loadScripts.map(
                      script => `${_cdnPath}${script}`
                    ),
=======
                    loadScripts: manifest.loadScripts.map(script => {
                      // Only prefix if script is a relative path
                      if (
                        typeof script === 'string' &&
                        !/^([a-z][a-z0-9+\-.]*:)?\/\//i.test(script)
                      ) {
                        return `${_cdnPath}${script}`;
                      }
                      return script;
                    }),
>>>>>>> REPLACE

</suggested_fix>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread webpack/common/ScalprumModule/ScalprumContext.js
Comment thread webpack/common/ScalprumModule/ScalprumContext.js
Comment on lines +76 to +83
loadScripts: manifest.loadScripts.map(
script => `${_cdnPath}${script}`
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (bug_risk): transformPluginManifest mutates loadScripts URLs but assumes all scripts are relative.

This logic may fail if loadScripts includes absolute URLs. Please ensure only relative paths are prefixed, or clearly document the assumption that all scripts are relative.

Suggested change
loadScripts: manifest.loadScripts.map(
script => `${_cdnPath}${script}`
),
loadScripts: manifest.loadScripts.map(script => {
// Only prefix if script is a relative path
if (
typeof script === 'string' &&
!/^([a-z][a-z0-9+\-.]*:)?\/\//i.test(script)
) {
return `${_cdnPath}${script}`;
}
return script;
}),

@MariaAga

MariaAga commented Jul 2, 2025

Copy link
Copy Markdown
Member Author

Updated the docs comment with more info

@jeremylenz

Copy link
Copy Markdown
Collaborator

Once theforeman/foreman#10342 is in (and any conversations above are addressed and resolved) I think this should be ready to go

@ShimShtein

Copy link
Copy Markdown
Member

@MariaAga Do we need this one if we use theforeman/foreman#10598 ?

@MariaAga

Copy link
Copy Markdown
Member Author

This is a generic component, I think we should re-write the ScalprumProvider logic + config everytime instead of forcing this kind of generalization
The foreman pr shared the package itself
So the prs dont really share any code or logic

@ShimShtein

Copy link
Copy Markdown
Member

If you think it's a waste of effort, let's get rid of it. Do you have the "manual" for what we need to do in each of our PRs?

@MariaAga

Copy link
Copy Markdown
Member Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants