Skip to content

feat: Add prop version to plugins manifest to avoid browser caching#24153

Merged
gustavotrott merged 6 commits intobigbluebutton:v3.0.x-releasefrom
GuiLeme:avoid-plugin-cache
Oct 31, 2025
Merged

feat: Add prop version to plugins manifest to avoid browser caching#24153
gustavotrott merged 6 commits intobigbluebutton:v3.0.x-releasefrom
GuiLeme:avoid-plugin-cache

Conversation

@GuiLeme
Copy link
Collaborator

@GuiLeme GuiLeme commented Oct 28, 2025

What does this PR do?

This PR introduces support for the optional version directive in plugin manifests.
When defined, the plugin version is automatically appended as a query parameter to the javascriptEntrypointUrl during plugin loading (e.g., MyPlugin.js?version=0.0.8).

This change helps prevent browsers from caching outdated plugin scripts between deployments, ensuring that the latest version is always loaded without affecting plugin behavior.

Closes

Closes bigbluebutton/bigbluebutton-html-plugin-sdk#224

Motivation

Previously, when redeploying a plugin without changing its filename, browsers could continue serving a cached version of the JavaScript entry point. This often led to unexpected behavior or outdated logic being executed.

By adding the version directive, we ensure that each deployment produces a unique URL, prompting browsers to fetch the updated file instead of using the cached one.

How to test

  1. Add a version field in your plugin’s manifest, for example:
    {
      "version": "0.0.8"
    }
  2. Deploy the plugin.
  3. Open the browser’s developer tools and check the network tab — the plugin will now be loaded as:
    MyPlugin.js?version=0.0.8
    
  4. Update the version number and redeploy.
  5. Confirm that the new URL includes the updated version and that the browser fetches the new file (not cached).

No additional configuration is required.

More

  • Added/updated documentation (see version directive section in the plugin manifest docs)

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 28, 2025

Walkthrough

Added an optional field version: Option[String] = None to PluginManifestContent. Refactored entrypoint URL computation to first derive an absolute entrypoint URL when needed, then call a new private helper createFinalJavascriptEntrypointUrl(plugin, jsEntrypointAbsoluteUrl) which validates manifest.version (via Version.isValid), logs a warning for invalid values, and appends the version as a query parameter when valid. Updated documentation (docs/docs/plugins.md) to show the version field in manifest examples and to explain how the version is appended to javascriptEntrypointUrl.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant GraphQL
  participant PluginService
  participant Helper as createFinalJavascriptEntrypointUrl
  note right of Helper #E6F7FF: computes absolute URL if needed, validates manifest.version, appends version query param when valid

  Client->>GraphQL: request plugin data
  GraphQL->>PluginService: fetch PluginManifestContent
  PluginService->>Helper: createFinalJavascriptEntrypointUrl(plugin, jsEntrypointAbsoluteUrl)
  Helper->>Helper: if relative -> makeAbsoluteUrl(relative, plugin.baseUrl)
  alt manifest.version exists
    Helper->>Helper: Version.isValid(manifest.version)?
    alt valid
      Helper-->>PluginService: absoluteUrl + "?<param>=<version>"
    else invalid
      Helper-->>PluginService: absoluteUrl (and log warning)
    end
  else no version
    Helper-->>PluginService: absoluteUrl
  end
  PluginService-->>GraphQL: return plugin data (final entrypoint URL)
  GraphQL-->>Client: respond with plugin data
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Files to review:
    • akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Plugins.scala
    • docs/docs/plugins.md
  • Pay attention to:
    • exact query-parameter name/format used when appending version and URL encoding behavior
    • Version.isValid validation semantics and the logged warning message for invalid versions
    • handling of already-absolute vs relative entrypoint URLs and any edge cases with URIBuilder usage

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues Check ✅ Passed The code changes in this PR successfully implement all coding requirements from linked issue #224. The implementation adds an optional version field to PluginManifestContent [core models change], automatically appends the version as a query parameter to the javascriptEntrypointUrl via the new createFinalJavascriptEntrypointUrl helper function [URL handling logic], and performs this processing on the server/app side in akka-bbb-apps [server-side implementation]. The changes validate the version using Version.isValid and properly handle edge cases where the version is invalid or missing. Documentation has been updated to explain the version field and how it prevents caching.
Out of Scope Changes Check ✅ Passed The code changes are all directly related to the pluginVersion feature objective. The Scala modifications (importing URIBuilder, adding the version field, and implementing the version-appending logic) are core to the requirements. The documentation updates focus primarily on explaining the new version field and anti-cache behavior, though the manifest example was expanded to show additional fields like dataChannels, eventPersistence, remoteDataSources, and settingsSchema. These additional fields appear to be part of showing a more complete manifest example rather than introducing new features themselves, and documentation-only expansions are generally acceptable for providing better context and clarity.
Title Check ✅ Passed The pull request title "feat: Add prop version to plugins manifest to avoid browser caching" is directly related to the main change in the changeset. The title accurately reflects that a new version field was added to the PluginManifestContent class and that this field is used to append a version query parameter to the javascriptEntrypointUrl for cache-busting purposes. The title is specific, clear, and concise—it identifies both the change (adding a version property) and its benefit (preventing browser caching). The changeset encompasses the code implementation and documentation updates that align with this stated objective.
Description Check ✅ Passed The PR description clearly and accurately describes the changeset. It explains that the feature introduces an optional version directive in plugin manifests that automatically appends the version as a query parameter to the javascriptEntrypointUrl, which aligns directly with the code changes that added the version: Option[String] field to PluginManifestContent and implemented the createFinalJavascriptEntrypointUrl helper method. The description includes relevant context about the motivation (preventing browser caching), references the closed issue (#224), provides specific testing steps, and notes that documentation was updated. The description is sufficiently detailed and directly relates to all aspects of the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e96695 and 2576ff6.

📒 Files selected for processing (2)
  • akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Plugins.scala (3 hunks)
  • docs/docs/plugins.md (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-package (bbb-apps-akka)
🔇 Additional comments (3)
docs/docs/plugins.md (1)

181-231: LGTM! Clear and accurate documentation.

The documentation changes effectively describe the new pluginVersion feature:

  • Grammar correction improves readability
  • Manifest example properly demonstrates the field usage
  • The explanation clearly communicates the cache-busting purpose
  • Example URL format is helpful for understanding the behavior
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Plugins.scala (2)

42-42: LGTM! Proper field addition.

The pluginVersion field is correctly defined as Option[String] with a default of None, maintaining backward compatibility with existing plugin manifests.


85-86: LGTM! Good refactoring.

Extracting the URL construction logic into a dedicated helper function improves code organization and makes the version parameter logic easier to maintain.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Plugins.scala (1)

126-128: Minor: Fix indentation inconsistency.

Lines 126-128 have 2 extra spaces of indentation compared to the surrounding code.

Apply this diff to normalize the indentation:

     plugin.manifest.content.foreach { manifest =>
       manifest.pluginVersion.foreach { version =>
-        if (!Version.isValid(version)) {
-          logger.warn("pluginVersion for [{}] is not valid, ignoring...", manifest.name)
-        }
+      if (!Version.isValid(version)) {
+        logger.warn("pluginVersion for [{}] is not valid, ignoring...", manifest.name)
+      }
       }
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2576ff6 and 6850897.

📒 Files selected for processing (1)
  • akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Plugins.scala (3 hunks)
🔇 Additional comments (3)
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Plugins.scala (3)

42-42: LGTM!

The optional pluginVersion field is properly added with backward-compatible defaults.


85-86: LGTM!

The refactoring correctly delegates URL construction to the new helper method and properly updates the manifest content.


116-136: Excellent fix for the critical bug!

The for-comprehension now correctly uses a guard clause to filter invalid versions, ensuring maybeVersion is typed as Option[String] instead of Option[Any]. The validation warning has been properly moved to a separate foreach block. This implementation resolves the type-safety issue from the previous review.

Based on learnings

@antobinary antobinary added this to the Release 3.0 milestone Oct 28, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Plugins.scala (1)

118-138: Version validation logic is correct; consider simplifying the warning code.

The validation logic correctly uses a guard clause in the for-comprehension to ensure maybeVersion is Option[String], and URIBuilder.addParameter properly handles existing query parameters. However, the warning logic can be simplified.

Apply this diff to simplify the warning code:

   plugin.manifest.content.foreach { manifest =>
     manifest.pluginVersion.foreach { version =>
-        if (!Version.isValid(version)) {
-          logger.warn("pluginVersion for [{}] is not valid, ignoring...", manifest.name)
-        }
-      }
+      if (!Version.isValid(version)) {
+        logger.warn("pluginVersion for [{}] is not valid, ignoring...", manifest.name)
+      }
+    }
   }

This removes unnecessary indentation while maintaining the same functionality.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6850897 and 7586649.

📒 Files selected for processing (2)
  • akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Plugins.scala (4 hunks)
  • docs/docs/plugins.md (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-package (bbb-apps-akka)
🔇 Additional comments (4)
docs/docs/plugins.md (2)

187-187: LGTM! Clear optional field indicator.

The pluginVersion field is properly documented as optional in the manifest example, consistent with the pattern used for other optional fields.


225-232: LGTM! Concise and clear documentation.

The pluginVersion documentation effectively explains the cache-busting behavior with a clear example. The brevity aligns with previous review feedback.

akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Plugins.scala (2)

43-43: LGTM! Optional field properly defined.

The pluginVersion field is correctly defined as Option[String] with a None default, making it truly optional for plugin manifests.


86-87: Integration looks correct, pending makeAbsoluteUrl fix.

The integration correctly replaces relative JavaScript entrypoint URLs with absolute URLs including the version parameter when applicable. However, correctness depends on the makeAbsoluteUrl fix noted in the earlier comment.

@GuiLeme GuiLeme force-pushed the avoid-plugin-cache branch from 7586649 to 80a2db8 Compare October 29, 2025 12:09
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Plugins.scala (1)

113-116: Prefer robust URL resolution for relative paths.

String concat won’t normalize "../" and may produce "//" when relativeUrl starts with "/". Use URI.resolve for correctness.

-  private def makeAbsoluteUrl(plugin: Plugin, relativeUrl: String): String = {
-    val baseUrl = plugin.manifest.url.substring(0, plugin.manifest.url.lastIndexOf('/') + 1)
-    baseUrl + relativeUrl
-  }
+  private def makeAbsoluteUrl(plugin: Plugin, relativeUrl: String): String = {
+    val manifestUri = java.net.URI.create(plugin.manifest.url)
+    manifestUri.resolve(relativeUrl).toString
+  }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7586649 and 80a2db8.

📒 Files selected for processing (2)
  • akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Plugins.scala (4 hunks)
  • docs/docs/plugins.md (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/docs/plugins.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-package (bbb-apps-akka)
🔇 Additional comments (2)
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Plugins.scala (2)

11-11: Good choice: use URIBuilder for query handling.

This enables safe param encoding and preserves existing query parts.


43-43: Schema addition looks correct and backward‑compatible.

Optional pluginVersion defaults to None; safe for existing manifests.

@GuiLeme GuiLeme requested a review from gustavotrott October 29, 2025 12:40
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Plugins.scala (1)

115-118: Consider handling edge cases in relative URL resolution.

The current string-concatenation approach works for typical relative paths (MyPlugin.js, ./MyPlugin.js) but has edge cases:

  1. Leading slash: If relativeUrl starts with /, it should resolve from the server root, not the manifest directory. Currently baseUrl="https://example.com/plugins/foo/" + relativeUrl="/scripts/plugin.js" produces "https://example.com/plugins/foo//scripts/plugin.js" instead of "https://example.com/scripts/plugin.js".

  2. Protocol-relative URLs: URLs like //cdn.example.com/file.js would be concatenated incorrectly.

Since most plugins use simple relative paths, this is unlikely to cause issues in practice. If edge-case robustness is desired, consider adding a check:

 private def makeAbsoluteUrl(plugin: Plugin, relativeUrl: String): String = {
+  if (relativeUrl.startsWith("/") || relativeUrl.startsWith("//")) {
+    // Absolute path from root or protocol-relative
+    val manifestUrlBuilder = new URIBuilder(plugin.manifest.url)
+    val baseUrl = manifestUrlBuilder.getScheme + "://" + manifestUrlBuilder.getHost + 
+      (if (manifestUrlBuilder.getPort != -1) s":${manifestUrlBuilder.getPort}" else "")
+    return if (relativeUrl.startsWith("//")) manifestUrlBuilder.getScheme + ":" + relativeUrl else baseUrl + relativeUrl
+  }
   val baseUrl = plugin.manifest.url.substring(0, plugin.manifest.url.lastIndexOf('/') + 1)
   baseUrl + relativeUrl
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 80a2db8 and cfabd1a.

📒 Files selected for processing (1)
  • akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Plugins.scala (4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build-package (bbb-web)
  • GitHub Check: build-package (bbb-apps-akka)
🔇 Additional comments (4)
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Plugins.scala (4)

11-11: LGTM!

The URIBuilder import is necessary for the query parameter handling in createFinalJavascriptEntrypointUrl and ensures proper URL encoding.


43-43: LGTM!

The optional pluginVersion field is properly declared with a None default, ensuring backward compatibility with existing plugin manifests.


85-92: LGTM! Past concerns about absolute URLs have been addressed.

The refactored logic now correctly handles both relative and absolute entrypoint URLs:

  • Relative URLs are converted to absolute via makeAbsoluteUrl
  • Absolute URLs (e.g., CDN links) are passed through unchanged
  • Both types then flow through createFinalJavascriptEntrypointUrl to receive the version query parameter

This ensures cache-busting works for all plugin entrypoints, addressing the major concern from previous reviews.


119-138: LGTM! Past critical validation bug and major parameter handling concerns have been resolved.

The implementation correctly addresses previous review feedback:

  1. Validation logic fix (lines 120-124): The guard clause if Version.isValid(version) ensures maybeVersion is Option[String] rather than Option[Any]. Invalid versions produce None, preventing malformed query parameters like ?version=().

  2. Separate warning (lines 126-132): The warning for invalid versions is logged independently, keeping the for-comprehension clean and type-safe.

  3. setParameter usage (line 135): Replaces any existing version parameter instead of creating duplicates, ensuring a single canonical version parameter in the URL.

The function safely handles missing manifests, missing versions, and invalid versions through proper Option chaining and validation.

@gustavotrott
Copy link
Collaborator

gustavotrott commented Oct 30, 2025

Tested here it it's creating the new URL properly:

image
image

It should solve the issue! Thanks

@GuiLeme GuiLeme requested a review from gustavotrott October 30, 2025 19:26
@sonarqubecloud
Copy link

@github-actions
Copy link

Automated tests Summary

All the CI tests have passed!

@gustavotrott gustavotrott changed the title feat: Add pluginVersion support to avoid browser caching feat: Add prop version to plugins manifest to avoid browser caching Oct 31, 2025
@gustavotrott gustavotrott merged commit 851742e into bigbluebutton:v3.0.x-release Oct 31, 2025
32 checks passed
@gustavotrott
Copy link
Collaborator

@GuiLeme when you have a chance please include this prop to the manifest of the oficial plugins!

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.

Add plugin version to javascriptEntrypointUrl to prevent browser caching issues

3 participants