Skip to content

feat(new-webui): Mongo Framework Client#841

Closed
AVMatthews wants to merge 7 commits into
y-scope:mainfrom
AVMatthews:mongo-framework-client
Closed

feat(new-webui): Mongo Framework Client#841
AVMatthews wants to merge 7 commits into
y-scope:mainfrom
AVMatthews:mongo-framework-client

Conversation

@AVMatthews

@AVMatthews AVMatthews commented Apr 22, 2025

Copy link
Copy Markdown
Contributor

Description

Client for new framework built on MongoCDC using socket.io meant to replace our current usage of meteor's publish-subscribe framework.

Second PR to split #809

First PR is #814

Checklist

  • The PR satisfies the contribution guidelines.
  • This is a breaking change and that has been indicated in the PR title, OR this isn't a
    breaking change.
  • Necessary docs have been updated, OR no docs need to be updated.

Validation performed

Manually added collection to mongo replica and tested to see that the content of the collection could be requested through the client from the server.

Summary by CodeRabbit

  • New Features
    • Introduced real-time, reactive querying of MongoDB collections in the web UI, enabling live updates of log data.
    • Added a plain data display for "compression-jobs" with automatic updates as data changes.
  • Bug Fixes
    • None.
  • Chores
    • Updated dependencies and development tools, including ESLint enhancements and Socket.IO client support.
    • Changed the development server port to 8081 and enabled WebSocket proxying for improved connectivity.

@AVMatthews AVMatthews requested a review from a team as a code owner April 22, 2025 07:34
@coderabbitai

coderabbitai Bot commented Apr 22, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This update introduces a client-side abstraction for reactive MongoDB queries over a Socket.IO connection in the log-viewer-webui. New modules implement a MongoCollection class and reactive cursor logic, along with a React hook for consuming live data. The application's main component is refactored to use these new abstractions for fetching and displaying data. Supporting changes include the addition of required dependencies, ESLint configuration updates, and Vite server adjustments to enable WebSocket proxying and change the development port.

Changes

File(s) Change Summary
components/log-viewer-webui/client/package.json Added socket.io-client as a runtime dependency; added several ESLint-related and type definition packages as development dependencies.
components/log-viewer-webui/client/src/App.tsx Refactored to use new MongoCollection and useTracker hook for querying and displaying data; commented out previous UI and theming code; added logic to render query results as JSON.
components/log-viewer-webui/client/src/mongoCDCLib/MongoCollection.ts New module introducing the MongoCollection class for managing collection interactions and initializing socket connections; provides a find method returning a reactive cursor.
components/log-viewer-webui/client/src/mongoCDCLib/MongoCollectionCursor.ts New module defining MongoCollectionCursor, supporting query building and asynchronous result retrieval over a socket connection via toArray().
components/log-viewer-webui/client/src/mongoCDCLib/MongoCollectionReactiveCursor.ts New module defining MongoCollectionReactiveCursor, extending the cursor for reactive, real-time data updates and subscription management over sockets.
components/log-viewer-webui/client/src/mongoCDCLib/useTracker.tsx New React hook useTracker for subscribing to a reactive cursor and exposing its data as component state, with proper cleanup on unmount or dependency change.
components/log-viewer-webui/client/vite.config.ts Changed dev server port from 8080 to 8081, set host to "0.0.0.0"; added proxy for /socket.io/ to support WebSocket connections to the backend on port 3000.

Sequence Diagram(s)

sequenceDiagram
    participant App (React)
    participant MongoCollection
    participant MongoCollectionReactiveCursor
    participant Socket.IO Server

    App->>MongoCollection: new MongoCollection("collectionName")
    MongoCollection->>Socket.IO Server: emit "collection::init" (collectionName)
    App->>MongoCollection: find(query, options)
    MongoCollection->>MongoCollectionReactiveCursor: return new reactive cursor
    App->>MongoCollectionReactiveCursor: toReactiveArray(callbacks)
    MongoCollectionReactiveCursor->>Socket.IO Server: emit "collection::find::toReactiveArray" (query, options)
    Socket.IO Server-->>MongoCollectionReactiveCursor: emit "collection::find::update" (queryId, data)
    MongoCollectionReactiveCursor-->>App: callbacks.onData(data)
    Note over App: App re-renders with new data
    App->>MongoCollectionReactiveCursor: cleanup (unsubscribe)
    MongoCollectionReactiveCursor->>Socket.IO Server: emit "collection::find::unsubscribe" (queryId)
Loading

Suggested reviewers

  • junhaoliao
  • davemarco
✨ Finishing Touches
  • 📝 Generate Docstrings

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

❤️ Share
🪧 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 generate sequence diagram to generate a sequence diagram of the changes in 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.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (13)
components/log-viewer-webui/client/src/App.tsx (5)

1-1: Prefer explicit comparison over negation per coding guidelines

According to the coding guidelines, negated expressions should use explicit comparison.

While this is an ESLint directive rather than actual code logic, for consistency with your coding guidelines, consider:

-/* eslint-disable react/jsx-key */
+/* eslint-disable react/jsx-key */

Note: Instead of disabling the ESLint rule, consider addressing the underlying issue by adding keys to your mapped elements.


3-7: Consider removing commented-out code and imports

The commented-out imports suggest this is a work in progress. Consider removing them if they're no longer needed.

Keeping unused imports commented out can lead to code clutter. Either restore the components or remove the commented lines.


10-13: Unclear purpose of commented collection instance

There's a commented-out MongoReplicaCollection instance that may indicate incomplete implementation or testing code.

-// const collection2 = new MongoReplicaCollection("compression-jobs");

Remove this commented line if it's not needed, or add a comment explaining why it's kept for future reference.


40-46: Remove commented debug code before merging

The commented debug logging code should be removed before merging to production.

-/* for (let i = 0; i < results.length; i++) {
-    console.log(results[i]);
-}
-
-for (let i = 0; i < results2.length; i++) {
-    console.log(results2[i]);
-} */

Consider using a more structured approach to debugging, such as a debug flag or environment variable, if debugging is needed in the future.


63-65: Consider removing commented UI components

The commented UI components indicate incomplete migration to the new data layer.

If these components are no longer needed, remove them completely. If they will be reintroduced later, add a TODO comment explaining the plan.

components/log-viewer-webui/client/src/mongoCDCLib/useTracker.tsx (4)

9-19: Consider more specific type instead of unknown[]

The return type unknown[] is very generic. Consider using a more specific type if the structure of the returned data is known.

-const useTracker = (
-    getCursor: () => MongoCollectionReactiveCursor,
-    dependencies: object[] = []
-): unknown[] => {
+const useTracker = <T = unknown>(
+    getCursor: () => MongoCollectionReactiveCursor<T>,
+    dependencies: unknown[] = []
+): T[] => {

This would make the hook more type-safe and provide better intellisense for consumers.


20-21: Update state type to match return type

If you implement the generic type suggestion above, also update the state type.

-const [data, setData] = useState<unknown[]>([]);
+const [data, setData] = useState<T[]>([]);

31-33: Consider expanded error handling

The error handling logs to console but doesn't provide a way for the component to know about or handle the error.

Consider adding an error state that components can use to show error messages:

+const [error, setError] = useState<Error | null>(null);

const unsubscribe = cursor.toReactiveArray({
    onData: handleData,
    onError: (error: unknown) => {
        console.error("Error fetching data:", error);
+       setError(error instanceof Error ? error : new Error(String(error)));
    },
});

// And then adjust the return:
-return data;
+return { data, error };

39-40: ESLint rule disabling should be explained

When disabling ESLint rules, it's good practice to include a comment explaining why.

-    // eslint-disable-next-line react-hooks/exhaustive-deps
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+    // Using custom dependencies array instead of auto-detected deps
components/log-viewer-webui/client/src/mongoCDCLib/MongoCollectionCursor.ts (1)

93-97: Follow coding guidelines for conditionals

According to the coding guidelines, prefer false == <expression> over !<expression> pattern.

-                if (response.error) {
+                if (false == (response.error == null)) {
                     reject(response.error);

                     return;
                 }
components/log-viewer-webui/client/src/mongoCDCLib/MongoCollection.ts (1)

39-41: Convert TODO comments to tracked issues

Rather than keeping TODO comments in the code, consider creating actual issues in your issue tracker and reference them in the comments. This improves traceability and ensures these tasks aren't forgotten.

         // eslint-disable-next-line no-warning-comments
-        // TODO: use the server URL from the environment / constructor args
+        // TODO(#123): use the server URL from the environment / constructor args
         this.socket = getSharedSocket();

...

     // eslint-disable-next-line no-warning-comments
-    // TODO: add support for non-reactive cursors
+    // TODO(#124): add support for non-reactive cursors

Also applies to: 48-49

components/log-viewer-webui/client/src/mongoCDCLib/MongoCollectionReactiveCursor.ts (2)

44-47: Follow coding guidelines for conditionals

According to the coding guidelines, prefer false == <expression> pattern over negation or equality checks.

-                if (this.queryId === response.queryId) {
-                    if (response.error) {
+                if (false == (this.queryId !== response.queryId)) {
+                    if (false == (response.error == null)) {
                         return this.listener?.onError(response.error);
                     }

76-79: Simplify conditional with null check

While the current conditional follows the coding guidelines by using !==, it could be simplified for better readability.

-            if ("undefined" !== typeof response.queryId) {
+            if (null != response.queryId) {
                 unsubscribedInfo.queryId = response.queryId;
                 this.queryId = response.queryId;
             }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 52c310b and 3a4190e.

⛔ Files ignored due to path filters (1)
  • components/log-viewer-webui/client/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (7)
  • components/log-viewer-webui/client/package.json (1 hunks)
  • components/log-viewer-webui/client/src/App.tsx (1 hunks)
  • components/log-viewer-webui/client/src/mongoCDCLib/MongoCollection.ts (1 hunks)
  • components/log-viewer-webui/client/src/mongoCDCLib/MongoCollectionCursor.ts (1 hunks)
  • components/log-viewer-webui/client/src/mongoCDCLib/MongoCollectionReactiveCursor.ts (1 hunks)
  • components/log-viewer-webui/client/src/mongoCDCLib/useTracker.tsx (1 hunks)
  • components/log-viewer-webui/client/vite.config.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}`: - Prefer `false == ` rather than `!`.

**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}: - Prefer false == <expression> rather than !<expression>.

  • components/log-viewer-webui/client/vite.config.ts
  • components/log-viewer-webui/client/src/App.tsx
  • components/log-viewer-webui/client/src/mongoCDCLib/MongoCollectionCursor.ts
  • components/log-viewer-webui/client/src/mongoCDCLib/MongoCollectionReactiveCursor.ts
  • components/log-viewer-webui/client/src/mongoCDCLib/useTracker.tsx
  • components/log-viewer-webui/client/src/mongoCDCLib/MongoCollection.ts
🧬 Code Graph Analysis (1)
components/log-viewer-webui/client/src/App.tsx (1)
components/log-viewer-webui/client/src/mongoCDCLib/useTracker.tsx (1)
  • useTracker (45-45)
🪛 Biome (1.9.4)
components/log-viewer-webui/client/src/App.tsx

[error] 51-51: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)


[error] 56-56: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

🔇 Additional comments (9)
components/log-viewer-webui/client/vite.config.ts (2)

9-10: Server binding configuration looks good but consider security implications

The configuration changes to bind the server to all network interfaces (0.0.0.0) and change the port to 8081 are appropriate for development. However, be aware that binding to 0.0.0.0 makes your development server accessible from other devices on your network.

For development environments, this is likely intentional. If this configuration will be used in production, consider restricting access appropriately.


18-22: WebSocket proxy configuration correctly implemented

The Socket.IO proxy configuration is well-structured with appropriate settings for WebSocket support (ws: true) and origin preservation (changeOrigin: true).

components/log-viewer-webui/client/src/App.tsx (2)

20-28: Query implementation for results looks good

The implementation of the reactive query using useTracker is well structured.


30-38: Potential duplicative query logic

The second query is very similar to the first but with a smaller ID range. Consider if this duplication is intentional and necessary.

Is there a specific reason for having two similar queries with different ID ranges? If this is just for testing or demonstration purposes, consider consolidating or commenting the purpose.

components/log-viewer-webui/client/src/mongoCDCLib/useTracker.tsx (3)

1-7: Well-structured imports

The imports are clean and well-organized.


22-39: Well-implemented effect with proper cleanup

The useEffect hook properly subscribes to the cursor and cleans up when unmounting.


42-45: Export is well structured

The named export is appropriate for this custom hook.

components/log-viewer-webui/client/package.json (2)

24-25: Socket.IO client dependency correctly added

The Socket.IO client dependency is correctly added to support the new WebSocket functionality.


28-38: ESLint and related dependencies are appropriate

The ESLint-related dependencies added are appropriate for enhancing code quality checks.

Comment on lines +49 to +54
<div>
{results.map((r) => (
<div>
{JSON.stringify(r)}
</div>
))}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Missing key prop in list rendering

React requires a unique "key" prop for each element in an array to optimize rendering performance.

Add a unique key to each rendered item:

-{results.map((r) => (
-    <div>
-        {JSON.stringify(r)}
-    </div>
-))}
+{results.map((r) => (
+    <div key={r._id || Math.random().toString()}>
+        {JSON.stringify(r)}
+    </div>
+))}

Ideally, use a stable identifier from your data like r._id if available.

📝 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
<div>
{results.map((r) => (
<div>
{JSON.stringify(r)}
</div>
))}
<div>
{results.map((r) => (
<div key={r._id || Math.random().toString()}>
{JSON.stringify(r)}
</div>
))}
</div>
🧰 Tools
🪛 Biome (1.9.4)

[error] 51-51: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

Comment on lines +55 to +59
{results2.map((r) => (
<div>
{JSON.stringify(r)}
</div>
))}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Missing key prop in second list rendering

Similar to the first map, this one also needs unique keys for React list rendering.

-{results2.map((r) => (
-    <div>
-        {JSON.stringify(r)}
-    </div>
-))}
+{results2.map((r) => (
+    <div key={r._id || Math.random().toString()}>
+        {JSON.stringify(r)}
+    </div>
+))}

Use a consistent approach to keys between both lists.

📝 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
{results2.map((r) => (
<div>
{JSON.stringify(r)}
</div>
))}
{results2.map((r) => (
<div key={r._id || Math.random().toString()}>
{JSON.stringify(r)}
</div>
))}
🧰 Tools
🪛 Biome (1.9.4)

[error] 56-56: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

this.socket.emit("collection::find::toArray", {
query: this.findQuery,
options: this.findOptions,
}, (response: {error?: Error; data?: Document[]}) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Define or import the Document type

The type annotation uses Document[] but there's no import or definition for the Document type. This could cause TypeScript compilation errors.

- }, (response: {error?: Error; data?: Document[]}) => {
+ }, (response: {error?: Error; data?: any[]}) => {

Alternatively, import the Document type from MongoDB types:

+ import { Document } from 'mongodb';
📝 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
}, (response: {error?: Error; data?: Document[]}) => {
}, (response: {error?: Error; data?: any[]}) => {

Comment on lines +17 to +20
if (!sharedSocket) {
// You can pass a URL here if needed (e.g., from environment vars)
sharedSocket = io();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Follow coding guidelines for conditionals and consider URL configuration

The conditional doesn't follow the coding guideline to prefer false == <expression>. Also, the socket initialization lacks a URL parameter which could be problematic if the server is not at the same origin.

-    if (!sharedSocket) {
+    if (false == (sharedSocket != null)) {
         // You can pass a URL here if needed (e.g., from environment vars)
-        sharedSocket = io();
+        const serverUrl = process.env.SOCKET_SERVER_URL || window.location.origin;
+        sharedSocket = io(serverUrl);
     }

Comment on lines +7 to +8
onData: (data: Document[]) => void;
onError: (error: Error) => void;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Define or import the Document type

The Document type is used in multiple places but is neither imported nor defined. This could cause TypeScript compilation errors.

Add an import for the Document type at the top of the file:

import {Socket} from "socket.io-client";
import MongoCollectionCursor from "./MongoCollectionCursor.js";
+ import { Document } from 'mongodb';

Alternatively, define an interface for it within this file.

Also applies to: 12-13, 43-43

Comment on lines +91 to +93

this.socket.off("collection::find::update");
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using a more specific event unsubscription

The current implementation removes the entire "collection::find::update" event listener which could affect other reactive cursors if multiple are used simultaneously. Consider using a more targeted approach.

            });

-            this.socket.off("collection::find::update");
+            // Remove just this instance's listener or use a more specific event name
+            this.listener = null;
+            this.queryId = null;
        };

Alternatively, consider using a unique event name per cursor or implementing a more sophisticated event routing system.

Committable suggestion skipped: line range outside the PR's diff.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
components/log-viewer-webui/client/src/mongoCDCLib/MongoCollectionReactiveCursor.ts (2)

19-20: Define or import the Document type

The Document type is used in multiple places but is neither imported nor defined. This could cause TypeScript compilation errors.

Also applies to: 24-25, 55-55


104-104: Consider using a more specific event unsubscription

The current implementation removes the entire "collection::find::update" event listener which could affect other reactive cursors if multiple are used simultaneously. Consider using a more targeted approach.

🧹 Nitpick comments (2)
components/log-viewer-webui/client/src/mongoCDCLib/MongoCollectionReactiveCursor.ts (2)

18-21: Consider consolidating duplicate interfaces

The Listener and ReactiveArrayCallback interfaces have identical structure. Consider consolidating them into a single interface to reduce duplication and improve maintainability.

interface Listener {
    onData: (data: Document[]) => void;
    onError: (error: Error) => void;
}

-interface ReactiveArrayCallback {
-    onData: (data: Document[]) => void;
-    onError: (error: Error) => void;
-}
+type ReactiveArrayCallback = Listener;

Also applies to: 23-26


75-106: Add method to refresh or invalidate cached data

The current implementation doesn't provide a way to manually refresh data or invalidate the cache. This could be useful in scenarios where data might be stale or when user actions should trigger a refresh.

Consider adding a refresh method:

+ /**
+  * Refreshes the data by re-querying the server.
+  */
+ refresh(): void {
+     if (null === this.queryId || null === this.listener) {
+         return;
+     }
+     
+     this.socket.emit("collection::find::refresh", {
+         queryId: this.queryId
+     });
+ }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a4190e and 0c2551c.

📒 Files selected for processing (3)
  • components/log-viewer-webui/client/src/mongoCDCLib/MongoCollection.ts (1 hunks)
  • components/log-viewer-webui/client/src/mongoCDCLib/MongoCollectionReactiveCursor.ts (1 hunks)
  • components/log-viewer-webui/client/src/mongoCDCLib/useTracker.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • components/log-viewer-webui/client/src/mongoCDCLib/useTracker.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/log-viewer-webui/client/src/mongoCDCLib/MongoCollection.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}`: - Prefer `false == ` rather than `!`.

**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}: - Prefer false == <expression> rather than !<expression>.

  • components/log-viewer-webui/client/src/mongoCDCLib/MongoCollectionReactiveCursor.ts

Comment on lines +75 to +94
toReactiveArray (callback: ReactiveArrayCallback): () => void {
const unsubscribedInfo : {queryId: number | null} = {queryId: null};

this.socket.emit("collection::find::toReactiveArray", {
query: this.findQuery,
options: this.findOptions,
}, (response: Response<{queryId: number}>) => {
if ("error" in response) {
callback.onError(new Error(response.error));

return;
}

if ("undefined" !== typeof response.data.queryId) {
unsubscribedInfo.queryId = response.data.queryId;
this.queryId = response.data.queryId;
}

this.listener = callback;
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for socket emit

The socket.emit operation doesn't have proper error handling for connection issues or timeouts. Consider adding timeout handling and connection error detection.

toReactiveArray (callback: ReactiveArrayCallback): () => void {
    const unsubscribedInfo : {queryId: number | null} = {queryId: null};

+   // Add a timeout handler to avoid hanging if server doesn't respond
+   const timeoutId = setTimeout(() => {
+       callback.onError(new Error("Server response timeout"));
+   }, 10000);

    this.socket.emit("collection::find::toReactiveArray", {
        query: this.findQuery,
        options: this.findOptions,
    }, (response: Response<{queryId: number}>) => {
+       clearTimeout(timeoutId);
+       
        if ("error" in response) {
            callback.onError(new Error(response.error));

            return;
        }

        if ("undefined" !== typeof response.data.queryId) {
            unsubscribedInfo.queryId = response.data.queryId;
            this.queryId = response.data.queryId;
        }

        this.listener = callback;
    });
📝 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
toReactiveArray (callback: ReactiveArrayCallback): () => void {
const unsubscribedInfo : {queryId: number | null} = {queryId: null};
this.socket.emit("collection::find::toReactiveArray", {
query: this.findQuery,
options: this.findOptions,
}, (response: Response<{queryId: number}>) => {
if ("error" in response) {
callback.onError(new Error(response.error));
return;
}
if ("undefined" !== typeof response.data.queryId) {
unsubscribedInfo.queryId = response.data.queryId;
this.queryId = response.data.queryId;
}
this.listener = callback;
});
toReactiveArray (callback: ReactiveArrayCallback): () => void {
const unsubscribedInfo : {queryId: number | null} = {queryId: null};
// Add a timeout handler to avoid hanging if server doesn't respond
const timeoutId = setTimeout(() => {
callback.onError(new Error("Server response timeout"));
}, 10000);
this.socket.emit("collection::find::toReactiveArray", {
query: this.findQuery,
options: this.findOptions,
}, (response: Response<{queryId: number}>) => {
clearTimeout(timeoutId);
if ("error" in response) {
callback.onError(new Error(response.error));
return;
}
if ("undefined" !== typeof response.data.queryId) {
unsubscribedInfo.queryId = response.data.queryId;
this.queryId = response.data.queryId;
}
this.listener = callback;
});
}

Comment on lines +53 to +66
this.socket.on(
"collection::find::update",
(response: {error?: Error; data?: Document[]; queryId: number}) => {
if (this.queryId === response.queryId) {
if (response.error) {
return this.listener?.onError(response.error);
}

return this.listener?.onData(response.data ?? []);
}

return null;
}
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for socket events

The event listener for "collection::find::update" doesn't have any error handling for malformed responses or unexpected data formats, which could lead to runtime errors.

this.socket.on(
    "collection::find::update",
    (response: {error?: Error; data?: Document[]; queryId: number}) => {
+       try {
            if (this.queryId === response.queryId) {
                if (response.error) {
                    return this.listener?.onError(response.error);
                }

                return this.listener?.onData(response.data ?? []);
            }

            return null;
+       } catch (error) {
+           console.error("Error processing collection update:", error);
+           this.listener?.onError(error instanceof Error ? error : new Error(String(error)));
+       }
    }
);
📝 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
this.socket.on(
"collection::find::update",
(response: {error?: Error; data?: Document[]; queryId: number}) => {
if (this.queryId === response.queryId) {
if (response.error) {
return this.listener?.onError(response.error);
}
return this.listener?.onData(response.data ?? []);
}
return null;
}
);
this.socket.on(
"collection::find::update",
(response: {error?: Error; data?: Document[]; queryId: number}) => {
try {
if (this.queryId === response.queryId) {
if (response.error) {
return this.listener?.onError(response.error);
}
return this.listener?.onData(response.data ?? []);
}
return null;
} catch (error) {
console.error("Error processing collection update:", error);
this.listener?.onError(
error instanceof Error ? error : new Error(String(error))
);
}
}
);

@davemarco

Copy link
Copy Markdown
Contributor

replaced by #892

@davemarco davemarco closed this May 9, 2025
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.

3 participants