Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(plugin-meetings): brbState #4110

Open
wants to merge 13 commits into
base: next
Choose a base branch
from
Open

Conversation

SomeBody16
Copy link
Contributor

@SomeBody16 SomeBody16 commented Feb 21, 2025

COMPLETES SPARK-610065

This pull request addresses

Users can quickly toggle the "step away" feature on and off through the web client. To prevent issues on the server side caused by processing too many rapid requests, it feels good to limit the amount of these requests. This logic could similar to how the mute/unmute microphone functionality works.

by making the following changes

Based on MuteState now there is BrbState that won't send request to server until first one finishes.

Change Type

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Tooling change
  • Internal code refactor

The following scenarios were tested

  • Samples app tests
  • Unit tests

I certified that

  • I have read and followed contributing guidelines

  • I discussed changes with code owners prior to submitting this pull request

  • I have not skipped any automated checks

  • All existing and new tests passed

  • I have updated the documentation accordingly


Make sure to have followed the contributing guidelines before submitting.

Copy link

coderabbitai bot commented Feb 21, 2025

📝 Walkthrough

Walkthrough

The changes introduce a new module for managing the "Be Right Back" (BRB) state in meeting sessions through the addition of the brbState.ts file. This file defines the BrbState class and the createBrbState function, which encapsulate BRB state management by synchronizing local and server states. The BrbState class includes methods for enabling or disabling the BRB state, updating the server based on client changes, and handling server notifications. The Meeting class is modified to include a brbState property, simplifying the previous BRB logic by delegating state management to the new module. Additionally, a test suite for BrbState is introduced, covering instantiation, state transitions, synchronization requests, and error handling for conditions such as multistream support and established WebRTC media connections.

Possibly related PRs

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

yarn install v1.22.22
[1/4] Resolving packages...
warning eslint@8.57.1: This version is no longer supported. Please see https://eslint.org/version-support for other options.
warning eslint > @humanwhocodes/config-array@0.13.0: Use @eslint/config-array instead
warning eslint > @humanwhocodes/config-array > @humanwhocodes/object-schema@2.0.3: Use @eslint/object-schema instead
warning eslint > file-entry-cache > flat-cache > rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
warning eslint > file-entry-cache > flat-cache > rimraf > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning eslint > file-entry-cache > flat-cache > rimraf > glob > inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
warning eslint-import-resolver-typescript > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning glob@7.2.3: Glob versions prior to v9 are no longer supported
warning intern > glob@7.1.7: Glob versions prior to v9 are no longer supported
warning intern > glob > inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
warning jasmine > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning jest > @jest/core > jest-config > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning jest > @jest/core > @jest/reporters > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning jest > @jest/core > jest-runtime > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning jest > @jest/core > @jest/transform > babel-plugin-istanbul > test-exclude > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning mocha > glob@7.2.0: Glob versions prior to v9 are no longer supported
warning mocha > glob > inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
warning sinon@9.2.4: 16.1.1
warning sinon > @sinonjs/samsam > lodash.get@4.4.2: This package is deprecated. Use the optional chaining (?.) operator instead.
warning wd > request@2.88.0: request has been deprecated, see request/request#3142
warning wd > archiver > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning wd > q@1.5.1: You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.

(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)
warning wd > request > uuid@3.4.0: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
warning wd > request > har-validator@5.1.5: this library is no longer supported
warning wd > archiver > archiver-utils > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning @babel/cli > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning @babel/plugin-proposal-async-generator-functions@7.20.7: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead.
warning @babel/plugin-proposal-class-properties@7.18.6: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.
warning @babel/plugin-proposal-export-namespace-from@7.18.9: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-export-namespace-from instead.
warning @babel/plugin-proposal-nullish-coalescing-operator@7.18.6: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.
warning @babel/plugin-proposal-object-rest-spread@7.20.7: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.
warning @babel/plugin-proposal-optional-chaining@7.21.0: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.
warning @babel/polyfill@7.12.1: 🚨 This package has been deprecated in favor of separate inclusion of a polyfill and regenerator-runtime (when needed). See the @babel/polyfill docs (https://babeljs.io/docs/en/babel-polyfill) for more information.
warning @babel/polyfill > core-js@2.6.12: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.
warning @babel/runtime-corejs2 > core-js@2.6.12: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.
warning babel-plugin-lodash > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning workspace-aggregator-87101d9e-a177-4b19-a557-b13b0aef9148 > eslint@8.57.1: This version is no longer supported. Please see https://eslint.org/version-support for other options.
warning workspace-aggregator-87101d9e-a177-4b19-a557-b13b0aef9148 > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning workspace-aggregator-87101d9e-a177-4b19-a557-b13b0aef9148 > sinon@9.2.4: 16.1.1
warning workspace-aggregator-87101d9e-a177-4b19-a557-b13b0aef9148 > @babel/plugin-proposal-async-generator-functions@7.20.7: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead.
warning workspace-aggregator-87101d9e-a177-4b19-a557-b13b0aef9148 > @babel/plugin-proposal-class-properties@7.18.6: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.
warning workspace-aggregator-87101d9e-a177-4b19-a557-b13b0aef9148 > @babel/plugin-proposal-export-namespace-from@7.18.9: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-export-namespace-from instead.
warning workspace-aggregator-87101d9e-a177-4b19-a557-b13b0aef9148 > @babel/plugin-proposal-nullish-coalescing-operator@7.18.6: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.
warning workspace-aggregator-87101d9e-a177-4b19-a557-b13b0aef9148 > @babel/plugin-proposal-object-rest-spread@7.20.7: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.
warning workspace-aggregator-87101d9e-a177-4b19-a557-b13b0aef9148 > @babel/plugin-proposal-optional-chaining@7.21.0: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.
warning workspace-aggregator-87101d9e-a177-4b19-a557-b13b0aef9148 > @babel/polyfill@7.12.1: 🚨 This package has been deprecated in favor of separate inclusion of a polyfill and regenerator-runtime (when needed). See the @babel/polyfill docs (https://babeljs.io/docs/en/babel-polyfill) for more information.
[2/4] Fetching packages...
error eslint-plugin-jsdoc@40.3.0: The engine "node" is incompatible with this module. Expected version "^14 || ^16 || ^17 || ^18 || ^19". Got "22.9.0"
error Found incompatible module.
info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a119cd2 and c5698f0.

📒 Files selected for processing (1)
  • packages/@webex/plugin-meetings/src/meeting/index.ts (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/@webex/plugin-meetings/src/meeting/index.ts
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build Packages
  • GitHub Check: AWS Amplify Console Web Preview

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@SomeBody16 SomeBody16 added the validated If the pull request is validated for automation. label Feb 21, 2025
@SomeBody16 SomeBody16 marked this pull request as ready for review February 21, 2025 10:47
@SomeBody16 SomeBody16 requested review from a team as code owners February 21, 2025 10:47
Copy link

@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

🔭 Outside diff range comments (1)
packages/@webex/plugin-meetings/src/meeting/index.ts (1)

738-739: 💡 Verification agent

🧩 Analysis chain

Verify BRB state cleanup.

The clearMeetingData method should clean up the brbState when the meeting ends to prevent memory leaks and ensure proper state management.


🏁 Script executed:

#!/bin/bash
# Search for cleanup of other state objects to verify if brbState needs similar cleanup
ast-grep --pattern 'clearMeetingData() {
  $$$
  this.$_ = null;
  $$$
}'

Length of output: 75


I'll run an additional verification to check the implementation details of the clearMeetingData method and locate any references to brbState in the file. This should help confirm whether the cleanup of brbState is handled correctly. For that, please execute the following composite shell script:

Once you verify the output of these commands, we'll be in a better position to decide if a cleanup is missing or if the behavior is already as expected.


🏁 Script executed:

#!/bin/bash
echo "Searching for clearMeetingData implementation context..."
rg -A 50 "clearMeetingData" packages/@webex/plugin-meetings/src/meeting/index.ts

echo "Searching for usages of 'brbState' within the file..."
rg "brbState" packages/@webex/plugin-meetings/src/meeting/index.ts

Length of output: 4849


Action Required: Ensure proper cleanup for brbState in clearMeetingData method.

The current implementation of clearMeetingData cleans up several state properties (like audio, video, and transcription) but does not include any cleanup for brbState. To prevent memory leaks and stale state issues, please add a cleanup step for brbState—for example, setting it to null (or applying any appropriate cleanup logic) at the end of the method. Review all related usages of brbState to ensure that resetting it does not cause unintended side effects elsewhere in the code.

🧹 Nitpick comments (6)
packages/@webex/plugin-meetings/src/meeting/index.ts (1)

3620-3622: Public method implementation looks good but needs documentation.

The beRightBack method correctly delegates to the brbState.enable() method, passing the required parameters. However, the method lacks proper JSDoc documentation.

+/**
+ * Manages be right back status updates for the current participant.
+ * 
+ * @param {boolean} enabled - Indicates whether the user enabled brb or not.
+ * @returns {Promise<void>} resolves when the brb status is updated or does nothing if not in a multistream meeting.
+ * @throws {Error} - Throws an error if the request fails.
+ * @public
+ */
 public async beRightBack(enabled: boolean): Promise<void> {
   return this.brbState.enable(enabled, this.sendSlotManager);
 }
packages/@webex/plugin-meetings/src/meeting/brbState.ts (4)

7-15: Add explicit return type annotation.

Consider adding an explicit return type annotation to improve code clarity and type safety.

-export const createBrbState = (meeting: Meeting, enabled: boolean) => {
+export const createBrbState = (meeting: Meeting, enabled: boolean): BrbState => {

23-31: Consider using readonly properties for state.

To prevent accidental state mutations, consider making the state properties readonly where appropriate.

-  state: {
+  private readonly state: {
     client: {
-      enabled: boolean;
+      enabled: boolean;
     };
     server: {
-      enabled: boolean;
+      enabled: boolean;
     };
     syncToServerInProgress: boolean;
   };

61-65: Add parameter validation.

Consider adding validation for the sendSlotManager parameter to prevent potential null pointer issues.

   public enable(enabled: boolean, sendSlotManager: SendSlotManager) {
+    if (!sendSlotManager) {
+      LoggerProxy.logger.error('Meeting:brbState#enable: sendSlotManager is required');
+      return;
+    }
     this.state.client.enabled = enabled;
     this.applyClientStateToServer(sendSlotManager);
   }

120-159: Improve error handling consistency and simplify Promise chain.

The error handling could be more consistent, and the Promise chain could be simplified using async/await.

   private async sendLocalBrbStateToServer(sendSlotManager: SendSlotManager) {
     const {enabled} = this.state.client;

     if (!this.meeting.isMultistream) {
-      const errorMessage =
-        'Meeting:brbState#sendLocalBrbStateToServer --> Not a multistream meeting';
-      const error = new Error(errorMessage);
-
-      LoggerProxy.logger.error(error);
-
-      return Promise.reject(error);
+      throw new Error('Meeting:brbState#sendLocalBrbStateToServer --> Not a multistream meeting');
     }

     if (!this.meeting.mediaProperties.webrtcMediaConnection) {
-      const errorMessage =
-        'Meeting:brbState#sendLocalBrbStateToServer --> WebRTC media connection is not defined';
-      const error = new Error(errorMessage);
-
-      LoggerProxy.logger.error(error);
-
-      return Promise.reject(error);
+      throw new Error('Meeting:brbState#sendLocalBrbStateToServer --> WebRTC media connection is not defined');
     }

     // this logic should be applied only to multistream meetings
-    return this.meeting.meetingRequest
-      .setBrb({
+    try {
+      await this.meeting.meetingRequest.setBrb({
         enabled,
         locusUrl: this.meeting.locusUrl,
         deviceUrl: this.meeting.deviceUrl,
         selfId: this.meeting.selfId,
-      })
-      .then(() => {
-        sendSlotManager.setSourceStateOverride(MediaType.VideoMain, enabled ? 'away' : null);
-      })
-      .catch((error) => {
-        LoggerProxy.logger.error('Meeting:brbState#sendLocalBrbStateToServer --> Error ', error);
-
-        return Promise.reject(error);
       });
+      sendSlotManager.setSourceStateOverride(MediaType.VideoMain, enabled ? 'away' : null);
+    } catch (error) {
+      LoggerProxy.logger.error('Meeting:brbState#sendLocalBrbStateToServer --> Error ', error);
+      throw error;
+    }
   }
packages/@webex/plugin-meetings/test/unit/spec/meeting/brbState.ts (1)

1-112: Add test cases for edge scenarios.

While the current test coverage is good, consider adding the following test cases:

  1. Error handling when setBrb request fails
  2. Race condition scenarios with multiple enable/disable calls
  3. Edge cases around the syncToServerInProgress flag
  4. Validation of error messages

Example test case for error handling:

it('handles setBrb request failure', async () => {
  meeting.meetingRequest.setBrb.rejects(new Error('Network error'));
  brbState.enable(true, meeting.sendSlotManager);
  await testUtils.flushPromises();

  assert.isFalse(brbState.state.syncToServerInProgress);
  assert.isTrue(meeting.meetingRequest.setBrb.calledOnce);
});
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 06a71aa and 4b0d1aa.

📒 Files selected for processing (3)
  • packages/@webex/plugin-meetings/src/meeting/brbState.ts (1 hunks)
  • packages/@webex/plugin-meetings/src/meeting/index.ts (5 hunks)
  • packages/@webex/plugin-meetings/test/unit/spec/meeting/brbState.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Determine Changed Packages
🔇 Additional comments (8)
packages/@webex/plugin-meetings/src/meeting/index.ts (7)

164-164: Import statement looks good.

The import of BrbState and createBrbState from ./brbState is properly defined.


650-650: Property declaration follows class field conventions.

The brbState property is correctly declared as an instance field of type BrbState.


3394-3407: Event handling for BRB state changes is properly implemented.

The event handler correctly:

  1. Delegates server BRB updates to the brbState instance
  2. Triggers appropriate events to notify clients about BRB state changes
  3. Follows the established pattern for event handling in the codebase

7387-7387: BRB state initialization follows established patterns.

The initialization of brbState using createBrbState follows the same pattern as other state objects like muteState, passing the required parameters.


3394-3407:

✅ Verification successful

Verify event trigger consistency.

The event trigger for BRB state changes follows the established pattern but we should verify if any additional events need to be triggered for analytics or metrics.


🏁 Script executed:

#!/bin/bash
# Search for similar event patterns to ensure consistency
rg "EVENT_TRIGGERS\.MEETING_SELF.*_UPDATE" packages/@webex/plugin-meetings/src/meeting/

Length of output: 301


Event trigger consistency confirmed.
The BRB state trigger follows the established pattern (as with MEETING_SELF_PHONE_AUDIO_UPDATE) and no additional analytics or metrics events are required.


8738-8739:

❓ Verification inconclusive

Verify BRB state cleanup in leave scenarios.

The clearMeetingData method is called during leave/end scenarios, but we should verify if brbState needs any special cleanup like unsubscribing from events.


🏁 Script executed:

#!/bin/bash
# Search for cleanup patterns in other state objects
rg -A 5 "\.off\(" packages/@webex/plugin-meetings/src/meeting/brbState.ts

Length of output: 75


Action: Confirm BRB state event listener cleanup in leave scenarios

The clearMeetingData method takes care of resetting the audio and video states, but it’s unclear whether any event listeners registered by the brbState require explicit cleanup (such as unsubscribing via .off). The initial grep for .off( in packages/@webex/plugin-meetings/src/meeting/brbState.ts returned no results, which may indicate that either no cleanup is needed or that event subscriptions use a different pattern.

Before moving forward, please manually verify whether:

  • The brbState module registers event listeners (e.g., using .on() that could accumulate if not properly cleaned up.
  • If such listeners exist, appropriate cleanup (similar to that for audio and video) is performed in leave scenarios (possibly in clearMeetingData or another cleanup method).

Once you confirm that either no event subscriptions exist or that the necessary cleanup is handled elsewhere, let us know if additional changes are required.


3620-3622:

❓ Verification inconclusive

Verify multistream support for BRB functionality.

The beRightBack method uses sendSlotManager which is specific to multistream meetings. We should verify if BRB functionality should be supported in non-multistream meetings as well.


🏁 Script executed:

#!/bin/bash
# Search for other features that require multistream support
ast-grep --pattern 'if (!this.isMultistream) {
  $$$
}'

Length of output: 66934


Action Required: Confirm BRB Behavior for Non-Multistream Meetings

The beRightBack method calls this.brbState.enable(enabled, this.sendSlotManager), and sendSlotManager is used elsewhere exclusively for multistream meetings (evidenced by various checks for if (!this.isMultistream) { ... }). Please verify if the BRB functionality is meant to function only when multistream is enabled. If BRB should also be supported in non-multistream meetings, consider whether an additional condition or alternate handling is required to avoid unintended side effects in those scenarios.

packages/@webex/plugin-meetings/src/meeting/brbState.ts (1)

180-202: LGTM! Clean and well-implemented state check methods.

The implementation is clear, concise, and follows good practices with proper return type annotations.

Comment on lines 73 to 112
private applyClientStateToServer(sendSlotManager: SendSlotManager) {
if (this.state.syncToServerInProgress) {
LoggerProxy.logger.info(
`Meeting:brbState#applyClientStateToServer: request to server in progress, we need to wait for it to complete`
);

return;
}

const remoteBrbRequiresSync = this.state.client.enabled !== this.state.server.enabled;

LoggerProxy.logger.info(
`Meeting:brbState#applyClientStateToServer: remoteBrbRequiresSync: ${remoteBrbRequiresSync}`
);

if (!remoteBrbRequiresSync) {
LoggerProxy.logger.info(
`Meeting:brbState#applyClientStateToServer: client state already matching server state, nothing to do`
);

return;
}

this.state.syncToServerInProgress = true;

this.sendLocalBrbStateToServer(sendSlotManager)
.then(() => {
this.state.syncToServerInProgress = false;
LoggerProxy.logger.info(
`Meeting:brbState#applyClientStateToServer: sync with server completed`
);

// need to check if a new sync is required, because this.state.client may have changed while we were doing the current sync
this.applyClientStateToServer(sendSlotManager);
})
.catch((e) => {
this.state.syncToServerInProgress = false;
LoggerProxy.logger.warn(`Meeting:brbState#applyClientStateToServer: error: ${e}`);
});
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve error handling and prevent potential stack overflow.

The recursive call to applyClientStateToServer could potentially lead to a stack overflow if the state keeps changing rapidly. Consider using a queue or debounce mechanism instead.

   private applyClientStateToServer(sendSlotManager: SendSlotManager) {
     if (this.state.syncToServerInProgress) {
       LoggerProxy.logger.info(
         `Meeting:brbState#applyClientStateToServer: request to server in progress, we need to wait for it to complete`
       );
       return;
     }

     const remoteBrbRequiresSync = this.state.client.enabled !== this.state.server.enabled;

     LoggerProxy.logger.info(
       `Meeting:brbState#applyClientStateToServer: remoteBrbRequiresSync: ${remoteBrbRequiresSync}`
     );

     if (!remoteBrbRequiresSync) {
       LoggerProxy.logger.info(
         `Meeting:brbState#applyClientStateToServer: client state already matching server state, nothing to do`
       );
       return;
     }

     this.state.syncToServerInProgress = true;

     this.sendLocalBrbStateToServer(sendSlotManager)
       .then(() => {
         this.state.syncToServerInProgress = false;
         LoggerProxy.logger.info(
           `Meeting:brbState#applyClientStateToServer: sync with server completed`
         );
-        // need to check if a new sync is required, because this.state.client may have changed while we were doing the current sync
-        this.applyClientStateToServer(sendSlotManager);
+        // Schedule the next sync check using setTimeout to prevent stack overflow
+        setTimeout(() => {
+          if (!this.state.syncToServerInProgress) {
+            this.applyClientStateToServer(sendSlotManager);
+          }
+        }, 0);
       })
       .catch((e) => {
         this.state.syncToServerInProgress = false;
-        LoggerProxy.logger.warn(`Meeting:brbState#applyClientStateToServer: error: ${e}`);
+        LoggerProxy.logger.error(`Meeting:brbState#applyClientStateToServer: error: ${e.message}`);
+        // Consider emitting an error event or notifying the user
       });
   }
📝 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
private applyClientStateToServer(sendSlotManager: SendSlotManager) {
if (this.state.syncToServerInProgress) {
LoggerProxy.logger.info(
`Meeting:brbState#applyClientStateToServer: request to server in progress, we need to wait for it to complete`
);
return;
}
const remoteBrbRequiresSync = this.state.client.enabled !== this.state.server.enabled;
LoggerProxy.logger.info(
`Meeting:brbState#applyClientStateToServer: remoteBrbRequiresSync: ${remoteBrbRequiresSync}`
);
if (!remoteBrbRequiresSync) {
LoggerProxy.logger.info(
`Meeting:brbState#applyClientStateToServer: client state already matching server state, nothing to do`
);
return;
}
this.state.syncToServerInProgress = true;
this.sendLocalBrbStateToServer(sendSlotManager)
.then(() => {
this.state.syncToServerInProgress = false;
LoggerProxy.logger.info(
`Meeting:brbState#applyClientStateToServer: sync with server completed`
);
// need to check if a new sync is required, because this.state.client may have changed while we were doing the current sync
this.applyClientStateToServer(sendSlotManager);
})
.catch((e) => {
this.state.syncToServerInProgress = false;
LoggerProxy.logger.warn(`Meeting:brbState#applyClientStateToServer: error: ${e}`);
});
}
private applyClientStateToServer(sendSlotManager: SendSlotManager) {
if (this.state.syncToServerInProgress) {
LoggerProxy.logger.info(
`Meeting:brbState#applyClientStateToServer: request to server in progress, we need to wait for it to complete`
);
return;
}
const remoteBrbRequiresSync = this.state.client.enabled !== this.state.server.enabled;
LoggerProxy.logger.info(
`Meeting:brbState#applyClientStateToServer: remoteBrbRequiresSync: ${remoteBrbRequiresSync}`
);
if (!remoteBrbRequiresSync) {
LoggerProxy.logger.info(
`Meeting:brbState#applyClientStateToServer: client state already matching server state, nothing to do`
);
return;
}
this.state.syncToServerInProgress = true;
this.sendLocalBrbStateToServer(sendSlotManager)
.then(() => {
this.state.syncToServerInProgress = false;
LoggerProxy.logger.info(
`Meeting:brbState#applyClientStateToServer: sync with server completed`
);
// Schedule the next sync check using setTimeout to prevent stack overflow
setTimeout(() => {
if (!this.state.syncToServerInProgress) {
this.applyClientStateToServer(sendSlotManager);
}
}, 0);
})
.catch((e) => {
this.state.syncToServerInProgress = false;
LoggerProxy.logger.error(`Meeting:brbState#applyClientStateToServer: error: ${e.message}`);
// Consider emitting an error event or notifying the user
});
}

Copy link

This pull request is automatically being deployed by Amplify Hosting (learn more).

Access this pull request here: https://pr-4110.d3m3l2kee0btzx.amplifyapp.com

Copy link

@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 (3)
packages/@webex/plugin-meetings/test/unit/spec/meeting/index.js (2)

3824-3825: Consider expanding test coverage for BrbState initialization.

While the basic setup is good, consider adding tests to verify:

  1. The createBrbState parameters (especially the false parameter)
  2. Error scenarios during BrbState initialization
 meeting.brbState = createBrbState(meeting, false);
 meeting.brbState.enable = sinon.stub().resolves();
+// Add test cases for initialization
+it('should initialize brbState with correct parameters', () => {
+  assert.strictEqual(meeting.brbState.initialState, false);
+});
+
+it('should handle errors during brbState initialization', () => {
+  const error = new Error('initialization failed');
+  createBrbState.throws(error);
+  assert.throws(() => meeting.brbState = createBrbState(meeting, false), error);
+});

3847-3860: Enhance test assertions for better coverage.

While the basic assertions are good, consider enhancing them to:

  1. Verify the parameters passed to enable
  2. Check the actual state after enable/disable operations
-assert.calledOnce(meeting.brbState.enable);
+assert.calledOnceWithExactly(meeting.brbState.enable, true);
+assert.strictEqual(meeting.brbState.isEnabled, true);

 it('should disable #beRightBack and return a promise', async () => {
   const brbResult = meeting.beRightBack(false);

   await brbResult;
   assert.exists(brbResult.then);
-  assert.calledOnce(meeting.brbState.enable);
+  assert.calledOnceWithExactly(meeting.brbState.enable, false);
+  assert.strictEqual(meeting.brbState.isEnabled, false);
 })
packages/@webex/plugin-meetings/src/meeting/brbState.ts (1)

73-80: Consider returning the promise for consistent chain handling.
Currently, if a request is already in progress, this branch returns Promise.resolve() immediately. You might want to store and return the ongoing Promise to maintain a single chain of resolution or rejection, ensuring any external callers can properly await and handle results or errors.

  if (this.state.syncToServerInProgress) {
    LoggerProxy.logger.info(
      `Meeting:brbState#applyClientStateToServer: request to server in progress, we need to wait for it to complete`
    );
-   return Promise.resolve();
+   return this.currentSyncPromise || Promise.resolve();
  }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4b0d1aa and a76b7eb.

📒 Files selected for processing (2)
  • packages/@webex/plugin-meetings/src/meeting/brbState.ts (1 hunks)
  • packages/@webex/plugin-meetings/test/unit/spec/meeting/index.js (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build Packages
  • GitHub Check: AWS Amplify Console Web Preview
🔇 Additional comments (4)
packages/@webex/plugin-meetings/test/unit/spec/meeting/index.js (1)

117-117: LGTM! Import statement correctly added.

The import for createBrbState is properly placed and aligns with the new BRB state management feature.

packages/@webex/plugin-meetings/src/meeting/brbState.ts (3)

1-6: Nice setup and imports.
The initial structure and descriptive imports establish a solid foundation for BRB state management without introducing extraneous dependencies.


105-107: Prevent potential stack overflow.
This direct recursive call to applyClientStateToServer reintroduces the risk of unbounded recursion if the state changes repeatedly. Previously, a setTimeout scheduling approach was suggested to break the recursion chain, which helps avoid stack overflows during rapid toggles.

-        this.applyClientStateToServer(sendSlotManager);
+        // Schedule the next sync check using a short delay
+        setTimeout(() => {
+          if (!this.state.syncToServerInProgress) {
+            this.applyClientStateToServer(sendSlotManager);
+          }
+        }, 0);

120-142: Verify partial fallback or alternative flows.
If the client is not in a multistream meeting or lacks a WebRTC media connection, the current logic rejects the operation. Confirm whether a fallback or partial enablement is ever desired (e.g., local-only BRB or a graceful no-op), especially if toggling BRB in non-multistream/partial connection scenarios could still make sense for specific use cases.

Copy link

@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

🔭 Outside diff range comments (1)
packages/@webex/plugin-meetings/test/unit/spec/meeting/index.js (1)

11890-11891: 🛠️ Refactor suggestion

Simplify complex test logic

The offset calculation logic in the payloadTestHelper function is overly complex and hard to understand:

if (share.length + 1 > offset) {
  offset = (offset + share.length + 1) / 2;
} else if (share.length + 1 < offset) {
  offset = share.length + 1 + 0.5;
}

Consider refactoring this to be more straightforward and easier to maintain.

🧹 Nitpick comments (15)
packages/@webex/plugin-meetings/test/unit/spec/meeting/index.js (15)

116-117: Add missing import for createBrbState

The test file is importing createBrbState from @webex/plugin-meetings/src/meeting/brbState but this import is missing from the imports section.

+import { createBrbState } from '@webex/plugin-meetings/src/meeting/brbState';

13824-13825: Fix test description to be more descriptive

The test description "should toggle the reactions with the right data and return a promise" is too vague. Consider making it more specific about what is being tested.

-'should toggle the reactions with the right data and return a promise'
+'should enable reactions by sending toggle request with enable=true when reactions are disabled'

13867-13868: Fix test description to be more descriptive

Similar to above, the test description "should resolve immediately if already enabled" could be more descriptive.

-'should resolve immediately if already enabled'
+'should resolve without sending toggle request when trying to enable already enabled reactions'

13879-13880: Fix test description to be more descriptive

Similar to above, the test description "should resolve immediately if already disabled" could be more descriptive.

-'should resolve immediately if already disabled'
+'should resolve without sending toggle request when trying to disable already disabled reactions'

13891-13892: Fix test description to be more descriptive

Similar to above, the test description "should toggle reactions on if controls is undefined and enable = true" could be more descriptive.

-'should toggle reactions on if controls is undefined and enable = true'
+'should enable reactions by sending toggle request when controls is undefined and enable=true'

12989-13023: Add missing test cases for buildLeaveFetchRequestOptions

The test coverage for buildLeaveFetchRequestOptions is minimal. Consider adding test cases for:

  1. When resourceId is not provided
  2. When reason is provided
  3. When both resourceId and reason are provided
  4. Error cases

13024-13152: Add missing test cases for getPermissionTokenExpiryInfo

The test coverage for getPermissionTokenExpiryInfo could be improved by adding test cases for:

  1. Invalid exp value (non-numeric string)
  2. Edge cases around time differences
  3. Handling of null/undefined permissionTokenReceivedLocalTime
  4. Handling of null/undefined permissionTokenPayload

13206-13246: Add missing test cases for roapMessageReceived

The test coverage for roapMessageReceived could be improved by adding test cases for:

  1. Different message types (OFFER, ERROR, etc.)
  2. Invalid message format
  3. Missing sdp field
  4. Error handling when getMediaServer fails
  5. Edge cases around multistream support

13154-13204: Add missing test cases for checkAndRefreshPermissionToken

The test coverage for checkAndRefreshPermissionToken could be improved by adding test cases for:

  1. Error handling when refreshPermissionToken fails
  2. Edge cases around timing thresholds
  3. Different ttl values
  4. Invalid input parameters

11902-11903: Fix test description to be more descriptive

The test description "Scenario #1: Whiteboard sharing as a webinar attendee" could be more descriptive about what is being tested.

-'Scenario #1: Whiteboard sharing as a webinar attendee'
+'Scenario #1: Verify remote share events are triggered when whiteboard is shared in webinar as attendee'

12031-12032: Fix test description to be more descriptive

The test description "Scenario #1: you share both whiteboards" could be more descriptive about what is being tested.

-'Scenario #1: you share both whiteboards'
+'Scenario #1: Verify share events when switching between two whiteboards shared by local user'

12032-12581: Improve test organization

The test file has a lot of nested describe blocks and repeated test setup. Consider:

  1. Moving common test setup to beforeEach blocks
  2. Extracting helper functions to reduce code duplication
  3. Organizing tests into smaller, focused files
  4. Using shared test fixtures
🧰 Tools
🪛 Biome (1.9.4)

[error] 12032-12231: Excessive describe() nesting detected.

Excessive nesting of describe() calls can hinder test readability.
Consider refactoring and reduce the level of nested describe to improve code clarity.

(lint/complexity/noExcessiveNestedTestSuites)


[error] 12233-12363: Excessive describe() nesting detected.

Excessive nesting of describe() calls can hinder test readability.
Consider refactoring and reduce the level of nested describe to improve code clarity.

(lint/complexity/noExcessiveNestedTestSuites)


[error] 12364-12551: Excessive describe() nesting detected.

Excessive nesting of describe() calls can hinder test readability.
Consider refactoring and reduce the level of nested describe to improve code clarity.

(lint/complexity/noExcessiveNestedTestSuites)


[error] 12553-12580: Excessive describe() nesting detected.

Excessive nesting of describe() calls can hinder test readability.
Consider refactoring and reduce the level of nested describe to improve code clarity.

(lint/complexity/noExcessiveNestedTestSuites)


12582-12747: Add missing test cases for keepAlive functionality

The test coverage for keepAlive functionality could be improved by adding test cases for:

  1. Race conditions in startKeepAlive/stopKeepAlive
  2. Error handling for network failures
  3. Edge cases around timing
  4. Retry logic
  5. State transitions

12748-12905: Add missing test cases for reactions functionality

The test coverage for reactions functionality could be improved by adding test cases for:

  1. Rate limiting
  2. Error handling for network failures
  3. Validation of reaction types
  4. State transitions
  5. Event handling

12906-12986: Add missing test cases for video mute functionality

The test coverage for video mute functionality could be improved by adding test cases for:

  1. Race conditions
  2. State transitions
  3. Error handling
  4. Event propagation
  5. Edge cases
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a76b7eb and c19b98d.

📒 Files selected for processing (1)
  • packages/@webex/plugin-meetings/test/unit/spec/meeting/index.js (4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build Packages
  • GitHub Check: AWS Amplify Console Web Preview

Copy link
Contributor

@edvujic edvujic left a comment

Choose a reason for hiding this comment

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

Overall looks good, left some comments. I agree with Anna that this should be a fix instead of a feat.

Copy link
Contributor

@brycetham brycetham left a comment

Choose a reason for hiding this comment

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

This looks pretty good! I had a few questions but they are non-blocking. Of course, it would be good to address everyone else's comments also.

Copy link

@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)
packages/@webex/plugin-meetings/test/unit/spec/meeting/brbState.ts (1)

81-87: Minor grammar issue: “won’t sent” → “won’t send.”
Please update the test description to fix the verb tense:

- it("won't sent request twice when in progress", async () => {
+ it("won't send request twice when in progress", async () => {
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c19b98d and e12b441.

📒 Files selected for processing (2)
  • packages/@webex/plugin-meetings/src/meeting/brbState.ts (1 hunks)
  • packages/@webex/plugin-meetings/test/unit/spec/meeting/brbState.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build Packages
  • GitHub Check: AWS Amplify Console Web Preview
🔇 Additional comments (20)
packages/@webex/plugin-meetings/test/unit/spec/meeting/brbState.ts (13)

1-3: Looks good.
No issues at a glance with these import statements or test helper setup.


4-6: All set here.
Test utilities import is straightforward and consistent with existing patterns.


7-10: No problems with describe or variable declarations.
Initialization approach looks tidy.


11-30: Clear and well-structured test setup.
The mock meeting object is adequately populated with necessary stubs.


32-45: Instantiation logic test looks solid.
It tests both enabled=true and enabled=false cases properly.


47-53: Well-defined “enable” behavior test.
Ensures local state and server update calls remain in sync.


55-61: Disable behavior is tested properly.
Covers the scenario where BRB is turned off and validated.


63-70: Grammar/style note: “won’t” vs “will not.”
This was suggested in a past review. If you prefer to keep the contraction, you can. Otherwise, consider “it('will not be able to send ...')” for consistency.


72-79: Generally consistent. No major issues.
Similar test to line 63-70 verifying webrtc connection constraints.


89-95: Sync behavior test is comprehensive.
Verifies minimal server updates and respects state changes.


97-103: Source state override test is robust.
Confirms the correct sendSlotManager call.


105-110: Handling server updates is properly covered.
Checks that local state changes when the server toggles the BRB state.


112-112: No further issues.
End of file looks tidy.

packages/@webex/plugin-meetings/src/meeting/brbState.ts (7)

1-5: Imports look appropriate.
References to logger, media core, and meeting type definitions are clear.


6-15: Factory function is straightforward.
Logging message is helpful for debugging.


17-50: Class structure and constructor are clear.
Storing separate client/server states is a clean approach.


52-63: Enable method logic is concise.
Successfully toggles local state and triggers server sync.


65-110: Recursive call may risk repeated sync triggers.
A previous review suggested preventing stack overflow or repeated calls if toggled rapidly. Since this was already raised, tagging as repeated feedback.


112-139: Local brb state server updates are well-handled.
You correctly reject when conditions aren’t met (non-multistream or missing media connection).


159-181: Server updates & combined state check are understandable.
isEnabled() returning true if either client or server is true matches the intended design.

Copy link

@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

♻️ Duplicate comments (1)
packages/@webex/plugin-meetings/src/meeting/brbState.ts (1)

102-104: 🛠️ Refactor suggestion

Improve error handling and prevent potential stack overflow.

The recursive call to applyClientStateToServer could potentially lead to a stack overflow if the state keeps changing rapidly. Consider using setTimeout to break the call stack.

        // need to check if a new sync is required, because this.state.client may have changed while we were doing the current sync
-        this.applyClientStateToServer(sendSlotManager);
+        // Schedule the next sync check using setTimeout to prevent stack overflow
+        setTimeout(() => {
+          if (!this.state.syncToServerInProgress) {
+            this.applyClientStateToServer(sendSlotManager);
+          }
+        }, 0);
🧹 Nitpick comments (3)
packages/@webex/plugin-meetings/src/meeting/brbState.ts (3)

105-108: Improve error logging.

Consider enhancing the error logging with more details and using error level instead of warn for consistency with other error handling in this class.

      .catch((e) => {
        this.state.syncToServerInProgress = false;
-        LoggerProxy.logger.warn(`Meeting:brbState#applyClientStateToServer: error: ${e}`);
+        LoggerProxy.logger.error(`Meeting:brbState#applyClientStateToServer: error: ${e.message}`);
+        // Consider emitting an error event or notifying the user
      });

120-138: Consider refactoring error handling for better code maintainability.

Both error handling blocks follow the same pattern. Consider extracting this into a helper method to reduce code duplication.

  private async sendLocalBrbStateToServer(sendSlotManager: SendSlotManager) {
    const {enabled} = this.state.client;

+    const handleError = (message: string) => {
+      const errorMessage = `Meeting:brbState#sendLocalBrbStateToServer --> ${message}`;
+      const error = new Error(errorMessage);
+      LoggerProxy.logger.error(error);
+      return Promise.reject(error);
+    };
+
    if (!this.meeting.isMultistream) {
-      const errorMessage =
-        'Meeting:brbState#sendLocalBrbStateToServer --> Not a multistream meeting';
-      const error = new Error(errorMessage);
-
-      LoggerProxy.logger.error(error);
-
-      return Promise.reject(error);
+      return handleError('Not a multistream meeting');
    }

    if (!this.meeting.mediaProperties.webrtcMediaConnection) {
-      const errorMessage =
-        'Meeting:brbState#sendLocalBrbStateToServer --> WebRTC media connection is not defined';
-      const error = new Error(errorMessage);
-
-      LoggerProxy.logger.error(error);
-
-      return Promise.reject(error);
+      return handleError('WebRTC media connection is not defined');
    }

19-170: Add an explicit isEnabled method.

Consider adding an isEnabled method to provide a consistent way to check the BRB state.

// Add this method to the BrbState class
+  /**
+   * Checks if the BRB state is currently enabled
+   * 
+   * @returns {boolean} - true if BRB is enabled, false otherwise
+   */
+  public isEnabled(): boolean {
+    return this.state.client.enabled;
+  }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a1c6d31 and 6038c0e.

📒 Files selected for processing (2)
  • packages/@webex/plugin-meetings/src/meeting/brbState.ts (1 hunks)
  • packages/@webex/plugin-meetings/test/unit/spec/meeting/brbState.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build Packages
  • GitHub Check: AWS Amplify Console Web Preview
🔇 Additional comments (7)
packages/@webex/plugin-meetings/test/unit/spec/meeting/brbState.ts (3)

65-65: Consider updating the test name for consistency.

The test name "won't be able to send..." doesn't follow consistent naming patterns. Consider using "does not send..." instead.

-    it("won't be able to send local brb state to server if it is not a multistream meeting", async () => {
+    it("does not send local brb state to server if it is not a multistream meeting", async () => {

74-74: Consider updating the test name for consistency.

Similar to the previous test case, consider using consistent naming patterns.

-    it("won't be able to send local brb state to server if webrtc media connection is not defined", async () => {
+    it("does not send local brb state to server if webrtc media connection is not defined", async () => {

83-83: Consider updating the test name for consistency.

Maintain consistency in test naming conventions.

-    it("won't send request twice when in progress", async () => {
+    it("does not send request twice when in progress", async () => {
packages/@webex/plugin-meetings/src/meeting/brbState.ts (4)

16-18: Improve class documentation.

The class documentation could be more descriptive to clarify the purpose and behavior of the BRB state management.

-/** The purpose of this class is to manage the local and remote brb state
- * and make sure that the server state always matches the last requested state by the client.
- */
+/** The purpose of this class is to manage the local and remote brb state and make sure that the server state always matches the last requested state by the client.
+ * It prevents sending excessive requests to the server by tracking the sync status and ensuring that only one request is in progress at a time.
+ * This helps mitigate potential server-side issues from processing too many rapid requests.
+ */

164-169: Rename parameter for consistency.

Consider renaming the parameter from enabled to brbEnabled to match other naming conventions in the codebase.

-  public handleServerBrbUpdate(enabled?: boolean) {
+  public handleServerBrbUpdate(brbEnabled?: boolean) {
    LoggerProxy.logger.info(
-      `Meeting:brbState#handleServerBrbUpdate: updating server brb to (${enabled})`
+      `Meeting:brbState#handleServerBrbUpdate: updating server brb to (${brbEnabled})`
    );
-    this.state.server.enabled = !!enabled;
+    this.state.server.enabled = !!brbEnabled;
  }

79-83: Clarify the terminology around "remote" BRB state.

The variable name remoteBrbRequiresSync might be confusing as BRB doesn't have a concept of remote control like mute does. Consider renaming for clarity.

-    const remoteBrbRequiresSync = this.state.client.enabled !== this.state.server.enabled;
+    const serverSyncRequired = this.state.client.enabled !== this.state.server.enabled;

    LoggerProxy.logger.info(
-      `Meeting:brbState#applyClientStateToServer: remoteBrbRequiresSync: ${remoteBrbRequiresSync}`
+      `Meeting:brbState#applyClientStateToServer: serverSyncRequired: ${serverSyncRequired}`
    );

-    if (!remoteBrbRequiresSync) {
+    if (!serverSyncRequired) {

149-149:

❓ Verification inconclusive

Ensure the correct pattern for SendSlotManager.

Make sure the setSourceStateOverride call follows the correct pattern expected by the SendSlotManager class. Verify that 'away' is a valid state.


🏁 Script executed:

#!/bin/bash
# Check how setSourceStateOverride is used in the codebase

ast-grep --pattern $'setSourceStateOverride($_, $_)'

Length of output: 53


Action Required: Manual Verification of setSourceStateOverride Usage

The current call in
packages/@webex/plugin-meetings/src/meeting/brbState.ts at line 149

sendSlotManager.setSourceStateOverride(MediaType.VideoMain, enabled ? 'away' : null);

needs to be manually verified to ensure it adheres to the expected pattern defined in the SendSlotManager class. In particular, please confirm that:

  • The method signature of setSourceStateOverride in SendSlotManager accepts the provided parameters.
  • The string 'away' is a valid state according to the SendSlotManager implementation.

Additional searches (e.g., using AST and regex searches) have not produced conclusive evidence about the usage pattern. It is recommended to review the SendSlotManager definition and its valid states to confirm this usage.

Copy link

@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

♻️ Duplicate comments (1)
packages/@webex/plugin-meetings/test/unit/spec/meeting/brbState.ts (1)

74-81: 🛠️ Refactor suggestion

Test missing for 'false' webrtcMediaConnection case.

The test only verifies behavior when webrtcMediaConnection is undefined, but not when it's explicitly false. As noted in previous reviews, we should test both scenarios.

Consider adding an additional test case:

it('does not send local brb state to server if webrtc media connection is false', async () => {
  meeting.mediaProperties.webrtcMediaConnection = false;
  brbState.enable(true, meeting.sendSlotManager);
  brbState.handleServerBrbUpdate(true);
  await testUtils.flushPromises();

  assert.isTrue(meeting.meetingRequest.setBrb.notCalled);
});
🧹 Nitpick comments (1)
packages/@webex/plugin-meetings/test/unit/spec/meeting/brbState.ts (1)

18-19: Set webrtcMediaConnection more consistently for testing.

The property is set to true in the setup but tests change it to undefined. For better test isolation, consider initializing it differently or creating a helper to reset test state.

One approach could be:

meeting = {
  isMultistream: true,
  locusUrl: 'locus url',
  deviceUrl: 'device url',
  selfId: 'self id',
  mediaProperties: {
-   webrtcMediaConnection: true,
+   webrtcMediaConnection: undefined,
  },
  // ...
};

// Then in tests that need it:
// meeting.mediaProperties.webrtcMediaConnection = true;

Or create a helper to reset state between tests if needed.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6038c0e and dbe13c1.

📒 Files selected for processing (1)
  • packages/@webex/plugin-meetings/test/unit/spec/meeting/brbState.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build Packages
  • GitHub Check: AWS Amplify Console Web Preview
🔇 Additional comments (2)
packages/@webex/plugin-meetings/test/unit/spec/meeting/brbState.ts (2)

65-72: Standardize test naming conventions across the test suite.

The test case naming here doesn't follow the consistent pattern suggested in previous review comments. Consider following a single naming convention for all test cases.

- it('cannot send local brb state to server if it is not a multistream meeting', async () => {
+ it('does not send local brb state to server if it is not a multistream meeting', async () => {

1-115: Tests look comprehensive for the BrbState implementation.

The test suite provides good coverage of the BrbState functionality, testing initialization, state changes, server synchronization, and error conditions. The implementation follows best practices for asynchronous testing.

Copy link
Contributor

@antsukanova antsukanova left a comment

Choose a reason for hiding this comment

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

Small nits to handle, but overall logic lgtm, thanks for your work on that so far

Copy link

@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

♻️ Duplicate comments (2)
packages/@webex/plugin-meetings/test/unit/spec/meeting/brbState.ts (1)

74-81: 🛠️ Refactor suggestion

Add test for the case when webrtcMediaConnection is false

The current test only verifies behavior when webrtcMediaConnection is undefined, but not when it's explicitly false. This was raised in previous reviews - we should add a dedicated test for the false scenario.

it('does not send local brb state to server if webrtc media connection is not defined', async () => {
  meeting.mediaProperties.webrtcMediaConnection = undefined;
  brbState.enable(true, meeting.sendSlotManager);
  brbState.handleServerBrbUpdate(true);
  await testUtils.flushPromises();

  assert.isTrue(meeting.meetingRequest.setBrb.notCalled);
});

+it('does not send local brb state to server if webrtc media connection is false', async () => {
+  meeting.mediaProperties.webrtcMediaConnection = false;
+  brbState.enable(true, meeting.sendSlotManager);
+  brbState.handleServerBrbUpdate(true);
+  await testUtils.flushPromises();
+
+  assert.isTrue(meeting.meetingRequest.setBrb.notCalled);
+});
packages/@webex/plugin-meetings/src/meeting/brbState.ts (1)

102-104: 🛠️ Refactor suggestion

Prevent potential stack overflow with recursive call

The recursive call to applyClientStateToServer could potentially lead to a stack overflow if the state keeps changing rapidly. Consider using setTimeout to break the call stack.

- // need to check if a new sync is required, because this.state.client may have changed while we were doing the current sync
- this.applyClientStateToServer(sendSlotManager);
+ // need to check if a new sync is required, because this.state.client may have changed while we were doing the current sync
+ // use setTimeout to prevent potential stack overflow
+ setTimeout(() => {
+   if (!this.state.syncToServerInProgress) {
+     this.applyClientStateToServer(sendSlotManager);
+   }
+ }, 0);
🧹 Nitpick comments (4)
packages/@webex/plugin-meetings/src/meeting/brbState.ts (4)

16-18: Improve class documentation to clarify the concept of "remote" state

The current documentation might lead to confusion about what "remote" means in this context. It's about the server state rather than another user's BRB state.

-/** The purpose of this class is to manage the local and remote brb state
- * and make sure that the server state always matches the last requested state by the client.
+/** The purpose of this class is to manage the client and server BRB state
+ * and make sure that the server state always matches the last state requested by the client.
+ * Unlike mute functionality, BRB state can only be changed by the user themselves.

79-79: Clarify variable naming to avoid confusion

The variable remoteBrbRequiresSync might be confusing as "remote" could be interpreted as another user's state. Consider renaming to better reflect that this is checking if the server state needs updating.

-const remoteBrbRequiresSync = this.state.client.enabled !== this.state.server.enabled;
+const serverStateMismatch = this.state.client.enabled !== this.state.server.enabled;

Then update all references to this variable in the subsequent code as well.


129-137: Enhance condition to explicitly check for false webrtcMediaConnection

The current condition checks if webrtcMediaConnection is undefined, but not if it's explicitly false. Since both cases need to be handled, modify the check to be more explicit.

-if (!this.meeting.mediaProperties.webrtcMediaConnection) {
+// Explicitly check for both undefined and false values
+if (this.meeting.mediaProperties.webrtcMediaConnection === undefined || 
+    this.meeting.mediaProperties.webrtcMediaConnection === false) {
  const errorMessage =
-    'Meeting:brbState#sendLocalBrbStateToServer: WebRTC media connection is not defined';
+    'Meeting:brbState#sendLocalBrbStateToServer: WebRTC media connection is not available';
  const error = new Error(errorMessage);

  LoggerProxy.logger.error(error);

  return Promise.reject(error);
}

107-107: Improve error logging with structured details

The current error logging only includes the error object, which might not provide sufficient context when debugging. Consider adding more structured information.

-LoggerProxy.logger.warn(`Meeting:brbState#applyClientStateToServer: error: ${e}`);
+LoggerProxy.logger.warn(`Meeting:brbState#applyClientStateToServer: error syncing BRB state`, {
+  error: e.message || e,
+  clientState: this.state.client.enabled,
+  serverState: this.state.server.enabled,
+  meetingId: this.meeting?.id
+});
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dbe13c1 and a119cd2.

📒 Files selected for processing (2)
  • packages/@webex/plugin-meetings/src/meeting/brbState.ts (1 hunks)
  • packages/@webex/plugin-meetings/test/unit/spec/meeting/brbState.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Determine Changed Packages

@@ -3390,6 +3392,7 @@ export default class Meeting extends StatelessWebexPlugin {
});

this.locusInfo.on(LOCUSINFO.EVENTS.SELF_MEETING_BRB_CHANGED, (payload) => {
this.brbState.handleServerBrbUpdate(payload?.brb?.enabled);
Copy link
Collaborator

Choose a reason for hiding this comment

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

not sure if this is possible, but can Locus change our BRB state without us being in a meeting (so without Meeting.addMedia() being called and therefore with this.brbState being undefined)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Locus will notify us about self brb change only as a response to us changing brb so it should never be called with brbState being undefined, but just to make sure I've added ? so it won't break anything


return Promise.reject(error);
});
return this.brbState.enable(enabled, this.sendSlotManager);
Copy link
Collaborator

Choose a reason for hiding this comment

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

if someone calls beRightBack() before calling addMedia(), this.brbState will be undefined. Previously this case was taken care of by the check for this.mediaProperties.webrtcMediaConnection.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've readded checks to this method for this case

@SomeBody16 SomeBody16 requested a review from marcin-bazyl March 3, 2025 14:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
validated If the pull request is validated for automation.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants