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

Websocket general implementation #1314

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open

Websocket general implementation #1314

wants to merge 5 commits into from

Conversation

paulfalgout
Copy link
Member

@paulfalgout paulfalgout commented Sep 13, 2024

This is a simple example of how we can implement websocket messages across entities.. subscriptions would be managed per app context, and then can optionally be handled per-entity, but then the messages are also globally published for app-specific handling. But the main purpose is to updated invalidated FE cache contextually.

We could at some point extend updating the cache to any cached entity such that, if the user has loaded a model, it will be updated behind the scenes such that it would not need to be refetched on navigation.. we could potentially purge neglected cached models after a period of time as well... or keep a stack of possible subscriptions where as things that fall off are cleared.

But for now this seems reasonable.

This is a WIP as we negotiate message structure.

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced flow management with improved tracking of actions and progress.
    • New message handlers for state and owner changes in models.
    • Added WebSocket routing capabilities for improved real-time data handling in tests.
    • Introduced a new Cypress command to intercept WebSocket connections, enhancing testing capabilities.
  • Bug Fixes

    • Refactored methods to streamline flow progress updates and message handling.
  • Documentation

    • Updated configurations for WebSocket support and message handling improvements.
  • Tests

    • Introduced new Cypress command for routing WebSocket connections during tests.

Copy link

coderabbitai bot commented Sep 13, 2024

Walkthrough

The changes introduce a new function, fetchWebsocket, to manage WebSocket endpoint retrieval and enhance the configuration fetching process. The fetchConfig function is modified to accept an isForm parameter, altering its execution flow based on context. Additionally, the event listener in src/js/index.js has been updated to remove a hardcoded WebSocket URL, indicating a shift in connection management. New Cypress commands for routing WebSocket interactions are also added, improving testing capabilities for real-time data.

Changes

Files Change Summary
src/js/config.js Added fetchWebsocket function and modified fetchConfig to accept isForm parameter.
src/js/index.js Removed hardcoded WebSocket URL assignment from appConfig.
test/support/commands.js Added .routeWebsockets() in Cypress command overwrite for improved WebSocket handling.
test/support/websockets.js Introduced routeWebsockets command to intercept WebSocket API requests for testing.

Possibly related PRs

  • Add Websocket service #1301: This PR adds a WebSocket service and modifies the src/js/index.js file, which is directly related to the changes in the main PR that also involve modifications to the same file and the handling of WebSocket connections.
  • Restart a closed socket #1318: This PR enhances the WebSocket service's handling of closed sockets and modifies the src/js/services/ws.cy.js file, which is relevant to the main PR's introduction of the fetchWebsocket function and its implications for WebSocket management.

Poem

🐰 In the garden of code, changes bloom bright,
Flow and WebSockets dance in delight.
With actions that grow and messages clear,
Our app hops along, spreading cheer!
So here’s to the updates, both big and small,
A joyful leap forward, we celebrate all! 🌼


Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 43ecb16 and d60827d.

Files selected for processing (4)
  • src/js/config.js (2 hunks)
  • src/js/index.js (1 hunks)
  • test/support/commands.js (1 hunks)
  • test/support/websockets.js (1 hunks)
Files skipped from review as they are similar to previous changes (4)
  • src/js/config.js
  • src/js/index.js
  • test/support/commands.js
  • test/support/websockets.js

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 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.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

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.

src/js/services/ws.js Outdated Show resolved Hide resolved
@@ -47,7 +47,8 @@ const _Model = BaseModel.extend({
},
getOwner() {
const owner = this.get('_owner');
return Radio.request('entities', `${ owner.type }:model`, owner.id);
Copy link
Member Author

Choose a reason for hiding this comment

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

@nmajor25 this is specifically the pattern needed for author to support clinician or patient for the form history. Store.get(author.type) ensures the correct model and request where Radio.request('entities', ${ owner.type }:model`` does not.

Copy link

cypress bot commented Sep 13, 2024

RoundingWell Care Ops Frontend    Run #6768

Run Properties:  status check passed Passed #6768  •  git commit d60827d3ad: Add temporary websocket fetch for wss
Project RoundingWell Care Ops Frontend
Branch Review websocket
Run status status check passed Passed #6768
Run duration 02m 54s
Commit git commit d60827d3ad: Add temporary websocket fetch for wss
Committer Paul Falgout
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 311
View all changes introduced in this branch ↗︎

@coveralls
Copy link

coveralls commented Sep 16, 2024

Pull Request Test Coverage Report for Build f9dd93e0-6e79-44a6-8068-f6c4a0d2d6a5

Details

  • 16 of 28 (57.14%) changed or added relevant lines in 4 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage decreased (-0.3%) to 99.702%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/js/entities-service/entities/actions.js 2 4 50.0%
src/js/entities-service/entities/flows.js 2 4 50.0%
src/js/services/ws.js 1 4 25.0%
src/js/apps/patients/patient/flow/flow_app.js 11 16 68.75%
Totals Coverage Status
Change from base Build e18838f1-9102-4d68-95e1-bd73f30ffd33: -0.3%
Covered Lines: 5913
Relevant Lines: 5925

💛 - Coveralls

@paulfalgout
Copy link
Member Author

This still needs tests, but I think it's close

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: 2

Outside diff range and nitpick comments (2)
src/js/config.js (2)

10-17: LGTM! The new fetchWebsocket function aligns with the PR objectives.

The function retrieves the WebSocket endpoint from the server and updates the appConfig object accordingly, contributing to the foundational implementation of websocket messages across various entities within the application.

Consider the following suggestions for further improvement:

  1. Add error handling for the fetch request to gracefully handle any potential failures in retrieving the WebSocket configuration.
  2. Assess the impact of introducing the new appConfig.ws property on existing code that relies on the appConfig object. Ensure that the addition of this property does not introduce any unintended side effects or break existing functionality.

Line range hint 19-36: LGTM! The modifications to fetchConfig introduce flexibility in the configuration fetching process.

The addition of the isForm parameter allows for different behaviors based on the context in which fetchConfig is invoked. If isForm is true, the success callback is executed immediately after fetching the main configuration, while if isForm is false, the function proceeds to call fetchWebsocket to retrieve the WebSocket configuration.

Consider the following suggestions for further improvement:

  1. Add code comments to clarify the purpose and behavior of the isForm parameter. Explain the different configuration fetching flows based on the value of isForm to enhance code readability and maintainability.
  2. Assess the impact of the modified control flow on existing code that relies on fetchConfig. Ensure that the introduction of the isForm parameter and the conditional execution of fetchWebsocket do not introduce any unintended side effects or break existing functionality.
Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 695837f and 43ecb16.

Files selected for processing (9)
  • src/js/apps/patients/patient/flow/flow_app.js (4 hunks)
  • src/js/base/model.js (2 hunks)
  • src/js/config.js (2 hunks)
  • src/js/entities-service/entities/actions.js (2 hunks)
  • src/js/entities-service/entities/flows.js (2 hunks)
  • src/js/index.js (0 hunks)
  • src/js/services/ws.js (2 hunks)
  • test/support/commands.js (1 hunks)
  • test/support/websockets.js (1 hunks)
Files not reviewed due to no reviewable changes (1)
  • src/js/index.js
Additional comments not posted (17)
test/support/websockets.js (1)

82-92: Great addition for testing WebSocket interactions!

The new routeWebsockets Cypress command is a valuable addition to the testing capabilities. It allows tests to intercept and control the responses from the WebSocket API, enabling more comprehensive testing scenarios.

By mocking the WebSocket API responses, tests can simulate different states and behaviors, improving the overall test coverage and reliability. This command will be particularly useful for testing WebSocket-related features and ensuring their proper functionality.

The implementation looks clean and straightforward, making it easy to understand and use in tests.

src/js/services/ws.js (2)

4-4: LGTM!

The import statement for Store from backbone.store is syntactically correct and aligns with the purpose mentioned in the AI-generated summary.


68-72: Excellent enhancement for handling WebSocket messages with resources!

The added logic in the _onMessage method aligns perfectly with the AI-generated summary. It enables the application to dynamically manage resources based on incoming WebSocket messages by:

  1. Checking for the presence of a resource property in the message.
  2. Retrieving the appropriate resource type from the Store using data.resource.type.
  3. Creating a new instance of the retrieved resource type using data.resource.id.
  4. Invoking the handleMessage method on the resource instance with the parsed message data.

This enhancement allows for resource-specific processing of WebSocket messages, improving the application's ability to handle and manage resources dynamically.

test/support/commands.js (1)

84-84: LGTM! The changes enhance WebSocket support in Cypress tests.

The addition of the .routeWebsockets() and .mockWs() methods within the overwritten visit command enables the routing and mocking of WebSocket connections during the visit process. This enhancement allows for testing real-time data or interactions that may occur on the page being visited.

By establishing the WebSocket routing before executing the original visit logic, the changes potentially improve the reliability and responsiveness of tests that depend on WebSocket communication.

The original visit function is still wrapped with a timeout of 60000 milliseconds, which is the default pageLoadTimeout for Cypress visits, ensuring that the visit process does not exceed the specified timeout.

Overall, these changes provide a solid foundation for handling WebSocket connections in Cypress tests and align with the PR objectives of implementing WebSocket messages across various entities within the application.

src/js/entities-service/entities/flows.js (2)

18-25: LGTM!

The new message handlers OwnerChanged and StateChanged are implemented correctly. They update the model attributes based on the received data and merge additional attributes using the spread operator. The use of object destructuring and default values for attributes is a good practice.


37-38: LGTM!

The modification to the getOwner method simplifies the retrieval of owner entities by directly instantiating a new owner object using the Store.get method. This change streamlines the retrieval logic and potentially improves performance.

src/js/entities-service/entities/actions.js (2)

23-30: LGTM!

The new message handlers OwnerChanged and StateChanged provide a clean and structured way for the _Model class to update its internal state based on changes to the owner and state. The handlers use object destructuring to extract the relevant data from the received message payload and update the model attributes using the set method, which triggers change events. The handlers also merge additional attributes provided in the message payload, ensuring that the model stays in sync with the latest owner and state information.

The addition of these message handlers enhances the functionality of the _Model class by enabling it to respond to specific events related to ownership and state changes.


58-59: Excellent improvement!

The modification to the getOwner method enhances the encapsulation of the owner retrieval process by directly creating an instance of the owner model based on its type and ID. It eliminates the need for a radio request to retrieve the owner model, reducing the coupling between the _Model class and the radio system.

This change aligns with the provided past review comment, which suggests using Store.get(owner.type) to ensure the correct model and request. The comment has been addressed effectively.

src/js/base/model.js (2)

73-81: Implementation of Overridden set Method is Correct

The overridden set method properly calls the original Backbone set method, checks for validation, updates the __cached_ts timestamp, and returns the model instance. This ensures that the model's data freshness is accurately tracked.


85-89: Message Handler Retrieval Logic is Well-Implemented

The _getMessageHandler method effectively retrieves the appropriate message handler based on the category. It accounts for handlers defined as functions or as method names on the model instance, enhancing flexibility in message processing.

src/js/apps/patients/patient/flow/flow_app.js (7)

19-21: Defining INCREMENT and DECREMENT constants improves code clarity

Defining INCREMENT and DECREMENT constants enhances readability and maintainability by avoiding magic numbers in the code.


77-80: Added 'message' event listener to flow

Adding the 'message' event listener allows the application to handle incoming messages related to the flow, enabling dynamic updates.


92-99: Correctly adding new action and updating flow progress

The _addAction method properly adds the action to the collections and updates the flow's progress.


109-109: Updating flow progress upon action state change

The call to this._setFlowProgress accurately updates the flow's progress based on the action's completion state.


114-114: Adjusting flow progress when an action is destroyed

The flow's progress counters are correctly decremented when an action is destroyed.


258-258: Adding action and updating flow progress after saving

Calling this._addAction(action); after saving the action ensures the new action is added and the flow progress is updated.


84-91: Undefined variables complete and total in _setFlowProgress

The variables complete and total are used without prior definition. Ensure they are retrieved from this.flow.get('_progress') before using them.

Apply this diff to fix the issue:

+      const { complete, total } = this.flow.get('_progress');

       this.flow.set({ _progress: {
         complete: complete + completeChange,
         total: total + totalChange,
       } });

Likely invalid or redundant comment.

Comment on lines +90 to +98
handleMessage({ category, timestamp, payload }) {
// Ignores messages that may be from recent local events
if (dayjs(this.get('__cached_ts')).add(10, 'seconds').isAfter(timestamp)) return;

const handler = this._getMessageHandler(category);
if (handler) handler.call(this, payload);

this.trigger('message', { category, payload });
},
Copy link

@coderabbitai coderabbitai bot Sep 19, 2024

Choose a reason for hiding this comment

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

Review Timestamp Comparison Logic in handleMessage

The timestamp comparison in handleMessage may be affected by differences between client and server clocks. Since __cached_ts is set using the client's current time, and timestamp likely originates from the server or other clients, discrepancies due to clock drift or time zone differences could lead to messages being incorrectly ignored.

Consider the following suggestions:

  • Ensure Consistent Timezones: Confirm that both __cached_ts and timestamp are in UTC and use the same format to minimize discrepancies.
  • Use Server Timestamps: Rely on server-generated timestamps for both caching and message handling to maintain consistency across clients.
  • Implement Tolerance Window: Introduce a reasonable time window or threshold to account for minor clock differences between systems.

Copy link
Member Author

Choose a reason for hiding this comment

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

The dates should be fine, and the tolerance window is add(10, 'seconds')

Copy link

Choose a reason for hiding this comment

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

@paulfalgout, understood. Thank you for the clarification regarding the dates and the 10-second tolerance window.

          (\_/)
          (•_•)
         />📝

Learnings added
Learnt from: paulfalgout
PR: RoundingWell/care-ops-frontend#1314
File: src/js/base/model.js:90-98
Timestamp: 2024-09-19T18:33:59.456Z
Learning: In the `handleMessage` method, there is already a 10-second tolerance window applied using `add(10, 'seconds')` to account for clock discrepancies.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +116 to +123
onFlowMessage({ category, payload }) {
if (category !== 'ActionCreated') return;
const { action } = payload;

if (this.actions.get(action.id)) return;

const fetchAction = Radio.request('entities', 'fetch:actions:model', action.id);
fetchAction.then(this._addAction);
Copy link

@coderabbitai coderabbitai bot Sep 19, 2024

Choose a reason for hiding this comment

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

Add error handling for fetchAction in onFlowMessage

The promise returned by fetchAction may fail if the action cannot be fetched. Consider adding error handling to manage rejections gracefully.

Apply this diff to handle errors:

        const fetchAction = Radio.request('entities', 'fetch:actions:model', action.id);
-       fetchAction.then(this._addAction);
+       fetchAction.then(this._addAction).catch(error => {
+         // Handle the error appropriately
+         console.error('Failed to fetch action:', error);
+       });

Committable suggestion was skipped due to low confidence.

Copy link
Member Author

Choose a reason for hiding this comment

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

not sure an error here would be very useful

Copy link

Choose a reason for hiding this comment

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

@paulfalgout, understood. Thanks for clarifying.

(._.)


Learnings added
Learnt from: paulfalgout
PR: RoundingWell/care-ops-frontend#1314
File: src/js/apps/patients/patient/flow/flow_app.js:116-123
Timestamp: 2024-09-19T18:34:21.887Z
Learning: In the `onFlowMessage` function in `flow_app.js`, adding error handling for `fetchAction` is not necessary even if the promise may fail.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@paulfalgout paulfalgout changed the title WIP Websocket general implementation \Websocket general implementation Sep 19, 2024
@paulfalgout paulfalgout changed the title \Websocket general implementation Websocket general implementation Sep 19, 2024
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.

2 participants