Skip to content

feat: Add aggregate query support#1041

Open
mskayali wants to merge 10 commits intoparse-community:masterfrom
mskayali:master
Open

feat: Add aggregate query support#1041
mskayali wants to merge 10 commits intoparse-community:masterfrom
mskayali:master

Conversation

@mskayali
Copy link

@mskayali mskayali commented Apr 30, 2025

Pull Request

Add Aggregate Query Support to Parse Flutter SDK

Issue

Closes: #664

Approach

This pull request introduces support for aggregate queries in the Parse Flutter SDK by implementing the ParseAggregate class. This enhancement allows developers to perform complex data aggregations directly within their Flutter applications, leveraging the Parse Server's /aggregate endpoint.​

Key Features:

​ParseAggregate Class: A new class that facilitates the construction and execution of aggregation pipelines.

​Pipeline Validation: Ensures that the provided aggregation pipeline is not empty, throwing an ArgumentError if validation fails.

​Seamless Integration: Utilizes existing ParseHTTPClient and handleResponse methods for consistent request handling and response parsing.

​Debug Support: Optional debug logging to assist developers during development and troubleshooting.​
Parse Community

Usage Example:

final res = await ParseAggregate('DietPlan', pipeline: {
  r'$match': {'Name': 'Ketogenic'}
}).execute();

print(res.result);

Notes:

This implementation aligns with the Parse REST API's aggregate query specifications.

Comprehensive documentation and unit tests have been included to ensure reliability and ease of use.

  • Add tests
  • Add changes to documentation (guides, repository pages, code comments)

Summary by CodeRabbit

  • New Features

    • Added aggregation and job/cloud-function invocation capabilities, plus a URL helper class and a standardized server response wrapper.
    • Switched live-query connection to a Socket.IO–based implementation.
  • Dependency Updates

    • Upgraded multiple Dart/Flutter package versions.
  • Bug Fixes

    • Expanded .gitignore to exclude additional build folders.
  • Other Improvements

    • Added endpoint constants and refined internal endpoint handling for cloud functions/jobs.

✏️ Tip: You can customize this high-level summary in your review settings.

@parse-github-assistant
Copy link

parse-github-assistant bot commented Apr 30, 2025

🚀 Thanks for opening this pull request!

@coderabbitai
Copy link

coderabbitai bot commented Apr 30, 2025

📝 Walkthrough

Walkthrough

Adds aggregate-query support and related server-function/job response handling to the Dart/Flutter Parse SDK: new ParseAggregate, ParseJobs, ParseResponse, constants and example usage, plus small utility and WebSocket/socket changes and dependency/version adjustments.

Changes

Cohort / File(s) Change Summary
Aggregation feature
packages/dart/lib/src/objects/parse_aggregate.dart, packages/dart/example/main.dart
Added ParseAggregate class and example usage showing pipeline creation, request, and printing ParseResponse.
Cloud functions / jobs
packages/dart/lib/src/objects/parse_jobs.dart, packages/dart/lib/src/objects/parse_function.dart
Added ParseJobs (POST execution, typed object variant); ParseCloudFunction now builds path using keyEndPointFunctions.
Response handling
packages/dart/lib/src/objects/parse_response.dart
New ParseResponse wrapper to normalize HTTP responses/results/errors.
SDK entry / part directives
packages/dart/lib/parse_server_sdk.dart
Updated part directives: commented old network aggregate part; added parse_aggregate.dart and parse_jobs.dart.
Constants
packages/dart/lib/src/base/parse_constants.dart
Added keyEndPointFunctions and keyEndPointJobs endpoint constants.
Utilities
packages/dart/lib/src/utils/url_replace.dart
New UrlReplace data container for URL components.
Networking / WebSocket
packages/dart/lib/src/network/parse_websocket_html.dart
Replaced html.WebSocket with Socket.IO client (IO.Socket); changed connect/readyState/close implementations and added state mapping.
HTTP client minor
packages/dart/lib/src/network/parse_http_client.dart
Minor import reordering and whitespace change (no logic change).
Manifests & ignores
packages/dart/pubspec.yaml, packages/flutter/pubspec.yaml, packages/flutter/example/.gitignore
Dependency version updates (web_socket_channel/socket_io_client/mime/timezone/package_info_plus/lints) and added .build/, .swiftpm/ to .gitignore.

Sequence Diagram(s)

sequenceDiagram
    participant App
    participant ParseAggregate
    participant ParseServer

    Note over App,ParseAggregate: Aggregate flow (new)
    App->>ParseAggregate: new ParseAggregate(functionName)
    App->>ParseAggregate: execute(pipeline)
    ParseAggregate->>ParseServer: GET /aggregate/{functionName}?pipeline=<json>
    Note right of ParseServer: Evaluate pipeline ($match, $group, ...)
    ParseServer-->>ParseAggregate: HTTP response (200/4xx/5xx + body)
    ParseAggregate-->>App: ParseResponse { success, statusCode, results, error }
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Several changes extend beyond aggregate query implementation: WebSocket migration from HTML to Socket.IO, dependency version upgrades, URL replacement utility class, and new Jobs endpoint support are out of scope for the stated aggregate query feature. Isolate aggregate query implementation into a focused PR. Move WebSocket migration, Jobs support, dependency upgrades, and utility classes to separate feature branches for independent review and testing.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'feat: Add aggregate query support' directly and clearly summarizes the main change—adding aggregate query functionality to the Parse SDK.
Description check ✅ Passed The PR description provides a detailed explanation of changes, approach, key features, usage example, and notes. However, the required template sections (Issue link format, and task completion status) are partially incomplete.
Linked Issues check ✅ Passed The PR successfully implements aggregate query support addressing issue #664's request for aggregate query documentation and implementation in the Flutter SDK.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

❤️ Share

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

Copy link

@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: 8

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ec71870 and c093ff1.

📒 Files selected for processing (8)
  • packages/dart/example/main.dart (1 hunks)
  • packages/dart/lib/parse_server_sdk.dart (2 hunks)
  • packages/dart/lib/src/network/parse_aggregate.dart (1 hunks)
  • packages/dart/lib/src/network/parse_http_client.dart (2 hunks)
  • packages/dart/lib/src/utils/url_replace.dart (1 hunks)
  • packages/dart/pubspec.yaml (2 hunks)
  • packages/flutter/example/.gitignore (1 hunks)
  • packages/flutter/pubspec.yaml (2 hunks)
🔇 Additional comments (10)
packages/flutter/example/.gitignore (2)

8-8: Approve addition of .build/ to .gitignore.
Ignoring the Flutter-generated .build/ directory is appropriate to keep build artifacts out of version control.


12-12: Approve addition of .swiftpm/ to .gitignore.
Excluding the Swift Package Manager metadata folder .swiftpm/ prevents unnecessary files from being committed.

packages/dart/lib/src/network/parse_http_client.dart (2)

5-5: Import ordering looks good

The reordering of the import for package:universal_io/io.dart to appear after package:parse_server_sdk/parse_server_sdk.dart follows a logical ordering pattern. This type of organization helps maintain consistency across the codebase.


33-33: Added whitespace for readability

The addition of a blank line at the start of the get method improves code readability by visually separating the method signature from its implementation.

packages/flutter/pubspec.yaml (1)

28-28:

Details

❓ Verification inconclusive

Verify compatibility with the major version bump

The parse_server_sdk dependency has been upgraded from ^6.4.0 to ^8.0.0, which is a major version bump. This suggests breaking changes could be present in the API.


🏁 Script executed:

#!/bin/bash
# Check for breaking changes or migration guides between versions
echo "Checking for breaking changes or migration guides..."
gh repo clone parse-community/Parse-SDK-Flutter tmp_repo
cd tmp_repo
git log --oneline v6.4.0..v8.0.0 | grep -i "breaking\|migration\|upgrade"

Length of output: 276


🏁 Script executed:

#!/bin/bash
# Locate the changelog and extract entries for v8.0.0 (and v7.x for context)
CL_FILE=$(find tmp_repo -maxdepth 2 -type f -iname "*changelog*.md" | head -n1)
echo "Using changelog file: $CL_FILE"
echo "=== v8.0.0 entries ==="
sed -n '/\bv8\.0\.0\b/,/^\s*##\s*v[0-9]\+\.[0-9]\+\.[0-9]\+\b/p' "$CL_FILE"
echo
echo "=== v7.x entries ==="
sed -n '/\bv7\.[0-9]\+\.[0-9]\+\b/,/^\s*##\s*v[0-9]\+\.[0-9]\+\.[0-9]\+\b/p' "$CL_FILE"

Length of output: 492


🏁 Script executed:

#!/bin/bash
# Clone the Flutter SDK repo and check for v8.0.0 tags and release notes
rm -rf tmp_repo
gh repo clone parse-community/Parse-SDK-Flutter tmp_repo
cd tmp_repo

echo "=== Tags matching v8.* ==="
git tag --list "v8.*"

echo
echo "=== Release notes for v8.0.0 ==="
# Try via GitHub CLI
gh release view v8.0.0 --repo parse-community/Parse-SDK-Flutter || echo "No release notes found via gh CLI"

echo
echo "=== Release body via GitHub API ==="
gh api repos/parse-community/Parse-SDK-Flutter/releases/tags/v8.0.0 --jq '.body' || echo "No release data found via API"

Length of output: 907


Confirm API compatibility for parse_server_sdk v8.0.0

No changelog or release notes could be found for v8.0.0 in the upstream Parse-SDK-Flutter repository, and the major bump from ^6.4.0 to ^8.0.0 likely introduces breaking changes. Please manually review and test before merging:

• Inspect the upstream repo (commits or code diff) between v6.x and v8.x for renamed or removed APIs
• Verify that every parse_server_sdk call in your code still compiles and behaves as expected
• Run or update integration/unit tests against SDK 8.0.0 to catch breaking changes early

packages/dart/pubspec.yaml (2)

37-38:

Details

✅ Verification successful

Verify compatibility with mime and timezone major version upgrades

Both mime (^1.0.0 to ^2.0.0) and timezone (^0.9.4 to ^0.10.1) have been upgraded to new major versions, which could introduce breaking changes.


🏁 Script executed:

#!/bin/bash
# Check if any code in the repository uses these packages directly
echo "Checking for direct usage of mime package..."
rg "package:mime" --type dart

echo "Checking for direct usage of timezone package..."
rg "package:timezone" --type dart

Length of output: 796


🏁 Script executed:

#!/bin/bash
# Inspect usage of mime package in parse_server_sdk.dart
echo "Searching for mime API calls..."
rg -n "lookupMimeType" -A2 -B2 --type dart packages/dart/lib/parse_server_sdk.dart || true
rg -n "extensionFromMime" -A2 -B2 --type dart packages/dart/lib/parse_server_sdk.dart || true

# Inspect usage of timezone API calls
echo "Searching for timezone API calls..."
rg -n "tz\." -A2 -B2 --type dart packages/dart/lib/parse_server_sdk.dart || true

Length of output: 418


No direct uses of mime or timezone APIs found – safe to upgrade

Both packages are imported in packages/dart/lib/parse_server_sdk.dart but no calls to lookupMimeType, extensionFromMime, or any tz.* members were detected. The major-version bumps therefore introduce no breaking changes in current code.

  • packages/dart/lib/parse_server_sdk.dart: imports only, no API invocation

45-45:

Details

✅ Verification successful

Updated linting rules

The lints dev dependency has been updated from ^4.0.0 to ^5.1.1. This may introduce new linting rules or deprecate existing ones.


🌐 Web query:

What are the changes in dart lints package between version 4.0.0 and 5.1.1?

💡 Result:

Changes in the Dart lints Package: Version 4.0.0 to 5.1.1

The Dart lints package defines the "core" and "recommended" sets of static analysis rules for Dart projects. Between versions 4.0.0 and 5.1.1, the primary changes are adjustments to supported SDK versions and some modifications to the included lint rules. Below is a detailed summary based on official changelogs:


Version 4.0.0

Released: Approximately 11 months ago
Minimum Dart SDK: 3.1

Major changes:

  • Core set:
    • Added:
      • library_annotations
      • no_wildcard_variable_uses
    • Removed:
      • package_prefixed_library_names
  • Recommended set:
    • Removed:
      • library_names
  • Other:
    • Updated the SDK lower-bound to 3.1.
    • Added a section in the README about upgrading to the latest lint set[3][6].

Version 5.0.0

Released: 7 months ago
Minimum Dart SDK: 3.5

Major changes:

  • Recommended set:
    • Updated the SDK lower-bound to 3.5.

No significant lint rule additions or removals were listed between 4.0.0 and 5.0.0; the main change was the minimum SDK requirement[3][6].


Version 5.1.0

Released: 7 months ago
Minimum Dart SDK: 3.6 (dev)

Major changes:

  • Core set:
    • Updated the SDK lower-bound to 3.6 (dev).

Version 5.1.1

Released: 4 months ago
Minimum Dart SDK: 3.6

Major changes:

  • Updated the SDK lower-bound to 3.6.
  • Moved to the dart-lang/core monorepo.
  • No lint rules were added or removed in this update[3][6].

Summary Table of Changes

Version Min SDK Notable Lint Rule Changes Other Changes
4.0.0 3.1 Added: library_annotations, no_wildcard_variable_uses
Removed: package_prefixed_library_names, library_names
Readme update
5.0.0 3.5 None Updated SDK lower-bound (recommended)
5.1.0 3.6 None Updated SDK lower-bound (core)
5.1.1 3.6 None Monorepo migration, SDK bound bump

Conclusion

  • The key lint rule changes occurred in version 4.0.0, where a few rules were added and removed from both the core and recommended sets.
  • Subsequent versions (5.0.0 to 5.1.1) primarily updated the minimum supported Dart SDK and made meta/organizational changes, without adjusting the lint set composition[3][6].
  • There were no breaking rule removals or additions in the lints package between 4.0.0 and 5.1.1, other than those made in 4.0.0.

For further details, you can review the official changelog, which contains links to the issues and discussions behind each lint rule change[3][6].

Citations:


Updated Dart lints Dev Dependency – Rule Changes Limited to v4.0.0

  • File: packages/dart/pubspec.yaml (line 45): updated lints from ^4.0.0^5.1.1
  • Version 4.0.0 introduced the only lint‐set changes:
    • Added: library_annotations, no_wildcard_variable_uses
    • Removed: package_prefixed_library_names, library_names
  • Versions 5.0.0 → 5.1.1 only raised the Dart SDK lower bound (to 3.6) and moved to the dart-lang/core monorepo—no lint rules were added or removed.

Please ensure any new lint violations from v4.0.0 have been addressed. Otherwise, this upgrade is safe to merge.

packages/dart/example/main.dart (1)

17-18: Whitespace organization looks good

The added blank lines help to visually separate the different sections of the example, improving readability.

Also applies to: 25-25

packages/dart/lib/parse_server_sdk.dart (2)

14-14: Import addition looks good.

The import for url_replace.dart is properly placed with the other imports and follows the package's import conventions.


37-37: Part directive correctly added.

The part directive for the new parse_aggregate.dart file is properly placed in the library file, maintaining the alphabetical ordering of the part directives.

sdk: flutter

flutter_lints: ^4.0.0
flutter_lints: ^5.0.0
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Updated linting rules

The flutter_lints dev dependency has been updated from ^4.0.0 to ^5.0.0. This may introduce new linting rules or deprecate existing ones.


🌐 Web query:

What are the changes in flutter_lints between version 4.0.0 and 5.0.0?

💡 Result:

Changes in flutter_lints Between Versions 4.0.0 and 5.0.0

Summary Table

Category 4.0.0 5.0.0
package:lints dependency 4.0.0 5.0.0
Minimum SDK Flutter 3.13 / Dart 3.1 Flutter 3.24 / Dart 3.5
Lints Added library_annotations
no_wildcard_variable_uses
invalid_runtime_check_with_js_interop_types
unnecessary_library_name
Lints Removed package_prefixed_library_names
library_names
avoid_null_checks_in_equality_operators
prefer_const_constructors
prefer_const_declarations
prefer_const_literals_to_create_immutables

Detailed Changes

Dependency and SDK Updates

  • The internal dependency was updated: package:lints upgraded from version 4.0.0 to 5.0.0.
  • Minimum required Flutter and Dart SDK versions increased to Flutter 3.24 and Dart 3.5[1][2][4].

New Lints Added

  • invalid_runtime_check_with_js_interop_types: Prevents incorrect runtime checks with interop types, improving JavaScript interoperability safety.
  • unnecessary_library_name: Discourages unnecessary library name declarations for cleaner code[1][4].

Lints Removed

  • avoid_null_checks_in_equality_operators: This removal gives developers more control over how null is handled in equality checks[1][4].
  • prefer_const_constructors
  • prefer_const_declarations
  • prefer_const_literals_to_create_immutables

These three "prefer_const_" lints were removed to simplify constant usage recommendations in Flutter projects, as they were sometimes seen as overly restrictive[1][4].

Removed in 4.0.0 (Still Absent in 5.0.0)

  • package_prefixed_library_names
  • library_names

These were already removed in the shift from 3.0.2 to 4.0.0[1].

Added in 4.0.0 (Still Present Unless Otherwise Stated)

  • library_annotations
  • no_wildcard_variable_uses

What This Means for Your Project

  • Upgrading to 5.0.0 may introduce new lint warnings related to JavaScript interop and unnecessary library name usage.
  • Code that previously failed the removed lints (prefer_const_* and avoid_null_checks_in_equality_operators) will no longer be flagged.
  • Projects must now use newer Flutter and Dart SDKs (at least Flutter 3.24 / Dart 3.5)[1][2][4].

Upgrading will keep your project aligned with the latest best practices and Dart ecosystem trends, and may lead to cleaner, more maintainable codebases[4].

Citations:


Update Dart/Flutter SDK Constraints & Address New Lint Rules

Please ensure the upgrade to flutter_lints: ^5.0.0 is fully supported:

• In packages/flutter/pubspec.yaml, bump your environment SDKs to meet the new minimums:

environment:
  sdk: ">=3.5.0 <4.0.0"
  flutter: ">=3.24.0"

• Run

flutter pub upgrade
flutter analyze

to surface the two new lints:

  • invalid_runtime_check_with_js_interop_types
  • unnecessary_library_name

• Review and adjust any code patterns that relied on lints now removed:

  • avoid_null_checks_in_equality_operators
  • prefer_const_constructors
  • prefer_const_declarations
  • prefer_const_literals_to_create_immutables
🤖 Prompt for AI Agents (early access)
In packages/flutter/pubspec.yaml at line 50, after updating flutter_lints to ^5.0.0, update the environment SDK constraints to sdk: ">=3.5.0 <4.0.0" and flutter: ">=3.24.0" to meet new minimum requirements. Then run flutter pub upgrade and flutter analyze to detect new lint warnings for invalid_runtime_check_with_js_interop_types and unnecessary_library_name. Finally, review and modify code to comply with these new lints and adjust any code relying on removed lints like avoid_null_checks_in_equality_operators and the prefer_const_* rules.

# Utils
path_provider: ^2.1.4
package_info_plus: ^5.0.1
package_info_plus: ^8.3.0
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Check for potential issues with package_info_plus upgrade

The package_info_plus dependency has been upgraded from ^5.0.1 to ^8.3.0, which is a significant version jump. Verify that this update is compatible with your app's requirements.


🌐 Web query:

Are there any breaking changes between package_info_plus v5.0.1 and v8.3.0?

💡 Result:

Breaking Changes Between package_info_plus v5.0.1 and v8.3.0

Overview:
Yes, there are breaking changes between package_info_plus v5.0.1 and v8.3.0. The most notable comes in the major release jump to v7.0.0 and subsequent versions.


Major Breaking Change in v7.0.0:

  • The Android implementation for the buildSignature value switched from using its previous hashing method to SHA-256 for improved security.

    "BREAKING FEAT(package_info_plus): Switch to SHA-256 for buildSignature on Android (#2835)"[1].

v5.0.1 Breaking Changes:

  • v5.0.1 itself introduced breaking changes related to handling the versionJsonUrl on web, now allowing for no page extension:

    "BREAKING FIX(package_info_plus): Allow no page extension in versionJsonUrl on web (#2381)"[3].

  • This requires you to adjust your web setup if you had previously relied on an explicit .json extension in URLs.

Other Notable Upgrades Between v5.0.1 → v8.3.0:

  • There are other minor and patch releases with additional fixes, features, and internal improvements, but the main breaking change of note is the SHA-256 switch for Android.
  • There is no mention in the changelogs of breaking API changes to the core PackageInfo API (such as constructor signatures, returned fields, or general usage) for end-users between these versions[1][3][8]. Most common usage patterns (fetching version, build number, etc.) remain stable.

Upgrade Considerations:

  • Android: If your logic depends on the buildSignature value, expect it to be different due to SHA-256.
  • Web: Ensure your Flutter web deployment does not rely on the .json extension for versionJsonUrl; update your deployment/server settings if needed.
  • General: Always clean and rebuild Xcode projects when upgrading, particularly for iOS apps, as incorrect version info may be cached in the build folder[8].

Summary Table of Key Breaking Changes

Version Breaking Change Description
5.0.1 versionJsonUrl can omit .json extension on web[3]
7.0.0 buildSignature uses SHA-256 on Android[1]

If you rely on either buildSignature for Android or serve web builds with a specific versionJsonUrl file extension, carefully review your implementation when upgrading between these versions.


Further Recommendations:

  • Review the full changelog for package_info_plus for all detailed updates between your installed version and the latest[1].
  • Test each platform you support (Android, iOS, Web) after upgrading to catch any behavioral nuances, especially around version/build info retrieval and signature computation.

Citations:


Review Breaking Changes in package_info_plus Upgrade

The jump from ^5.0.1 to ^8.3.0 introduces a couple of breaking changes you should verify:

  • Android (v7.0.0): buildSignature now uses SHA-256 instead of the previous hashing method. If your app logic depends on this value, update or re-test accordingly.
  • Web (v5.0.1): versionJsonUrl no longer requires a .json extension. Ensure your server or deployment doesn’t rely on a fixed .json suffix.

All other core PackageInfo APIs (version, build number, etc.) remain unchanged. Please review your implementation, adjust any dependent logic, and test each platform (Android, iOS, Web) after upgrading.

🤖 Prompt for AI Agents (early access)
In packages/flutter/pubspec.yaml at line 43, the package_info_plus dependency was upgraded from ^5.0.1 to ^8.3.0, which includes breaking changes. Review your app code for any usage of buildSignature on Android and update or retest it due to the switch to SHA-256 hashing in v7.0.0. Also, verify your web deployment does not rely on a .json extension in versionJsonUrl, adjusting server or deployment settings if necessary. Finally, test all platforms thoroughly to ensure compatibility with the new package version.

dio: ^5.7.0
http: ^1.2.0
web_socket_channel: ^2.4.3
web_socket_channel: ^3.0.3
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify compatibility with web_socket_channel major version upgrade

The web_socket_channel dependency has been upgraded from ^2.4.3 to ^3.0.3, which is a major version bump that may include breaking changes.


🌐 Web query:

What are the breaking changes in web_socket_channel v3.0.0?

💡 Result:

Breaking Changes in web_socket_channel v3.0.0

The 3.0.0 release of the Dart web_socket_channel package introduced several breaking changes that impact how developers interact with the API. The main breaking changes are as follows:

  • Removal of WebSocketChannel Constructor
    The direct constructor for WebSocketChannel has been removed. Developers can no longer instantiate the channel using WebSocketChannel() and must now use provided factory methods, such as WebSocketChannel.connect, to create channel instances[1][6].

  • WebSocketChannel Is Now an Abstract Interface
    The WebSocketChannel class has been converted into an abstract interface. This means you cannot instantiate it directly and must instead work with implementations provided by the package, such as via WebSocketChannel.connect[1][6].

  • Change in Exception Thrown by IOWebSocketChannel.ready
    The ready property on IOWebSocketChannel previously threw a WebSocketException on error. In v3.0.0, this now throws a WebSocketChannelException, requiring consumers to update their exception handling logic when awaiting the connection's readiness[1][6].

  • Adapter Around package:web_socket for Web Implementation
    An adapter for the package:web_socket package is now the default implementation for web platform connections made via WebSocketChannel.connect. This may affect the underlying behavior or compatibility for web-based applications[1][6].

Summary Table

Breaking Change Details
Constructor removal No direct WebSocketChannel() instantiation; use factory constructors instead
Abstract interface WebSocketChannel is now abstract; use specific implementations
Exception change in ready Throws WebSocketChannelException (previously WebSocketException)
Default web adapter Uses package:web_socket adapter for web via WebSocketChannel.connect

Developers upgrading to v3.0.0 should adjust their code to accommodate these changes[1][6].

Citations:


Validate Breaking Changes for web_socket_channel v3.0.0

We’ve bumped web_socket_channel from ^2.4.3 to ^3.0.3, which includes these breaking changes:

  • Removal of the direct WebSocketChannel() constructor – you must now use WebSocketChannel.connect(...).
  • WebSocketChannel is now an abstract interface.
  • IOWebSocketChannel.ready throws WebSocketChannelException (instead of WebSocketException).
  • Default web implementation now uses the package:web_socket adapter.

Please audit the Dart code for:

  • Any instantiation of WebSocketChannel() (replace with connect).
  • Direct uses of IOWebSocketChannel and calls to its ready property (update exception handling).
  • Web‐platform socket code relying on the old adapter.

Make the necessary updates or confirm that your code paths already comply with the new API.

🤖 Prompt for AI Agents (early access)
In packages/dart/pubspec.yaml at line 27, the web_socket_channel dependency has been upgraded from ^2.4.3 to ^3.0.3, which introduces breaking changes. Audit all Dart code that uses web_socket_channel for direct instantiation of WebSocketChannel() and replace these with WebSocketChannel.connect(...) factory methods. Also, update any exception handling around IOWebSocketChannel.ready to catch WebSocketChannelException instead of WebSocketException. Verify web platform socket code to ensure compatibility with the new package:web_socket adapter. Make these code adjustments to align with the new major version API.

Comment on lines 26 to 30
final res = await ParseAggregate('DietPlan', pipeline: {
r'$match': {'Name': 'Ketogenic'}
}).execute();

print(res);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance the ParseAggregate example with error handling and documentation

The example demonstrates the new aggregation query feature, but could be improved with error handling and better documentation.

Consider adding error handling and more descriptive output:

-  final res = await ParseAggregate('DietPlan', pipeline: {
-    r'$match': {'Name': 'Ketogenic'}
-  }).execute();
-
-  print(res);
+  // Execute an aggregate query to filter DietPlan objects
+  final aggregateResult = await ParseAggregate('DietPlan', pipeline: {
+    r'$match': {'Name': 'Ketogenic'}
+  }).execute();
+
+  if (aggregateResult.success) {
+    print("Aggregate query results:");
+    print(aggregateResult.results);
+  } else {
+    print("Aggregate query failed: ${aggregateResult.error?.message}");
+  }
🤖 Prompt for AI Agents (early access)
In packages/dart/example/main.dart around lines 26 to 30, the ParseAggregate example lacks error handling and sufficient documentation. Add try-catch blocks around the await call to handle potential exceptions gracefully. Include comments explaining the purpose of the aggregation query and the expected output. Also, enhance the print statement to provide more descriptive output for easier understanding of the results.

Comment on lines +1 to +24
class UrlReplace{
String? scheme;
String? userInfo;
String? host;
int? port;
String? path;
Iterable<String>? pathSegments;
String? query;
Map<String, dynamic>? queryParameters;
String? fragment;

UrlReplace({
this.scheme,
this.userInfo,
this.host,
this.port,
this.path,
this.pathSegments,
this.query,
this.queryParameters,
this.fragment,
});

} No newline at end of file
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add documentation and consider using Dart's Uri class instead.

This class lacks documentation explaining its purpose and usage. Additionally, it appears to duplicate functionality that's already available in Dart's Uri class.

  1. Add class-level and field-level documentation
  2. Add a space after the class name declaration: class UrlReplace {
  3. Consider whether this utility class is necessary or if Dart's Uri class could be used directly
-class UrlReplace{
+/// A container for URL components used for URL manipulation operations.
+/// This class provides a convenient way to store and pass URL components.
+class UrlReplace {
  /// The URL scheme (e.g., 'http', 'https')
  String? scheme;
  /// The user info portion of the URL
  String? userInfo;
  /// The host name
  String? host;
  /// The port number
  int? port;
  /// The path component of the URL
  String? path;
  /// The path segments as an iterable collection
  Iterable<String>? pathSegments;
  /// The query string
  String? query;
  /// The query parameters as a map
  Map<String, dynamic>? queryParameters;
  /// The fragment identifier
  String? fragment;

+  /// Creates a new UrlReplace instance with the specified URL components.
  UrlReplace({
    this.scheme,
    this.userInfo,
    this.host,
    this.port,
    this.path,
    this.pathSegments,
    this.query,
    this.queryParameters,
    this.fragment,
  });

}
🤖 Prompt for AI Agents (early access)
In packages/dart/lib/src/utils/url_replace.dart lines 1 to 24, add clear class-level documentation explaining the purpose of UrlReplace and field-level comments describing each property. Insert a space after the class name declaration to follow Dart style conventions (change `class UrlReplace{` to `class UrlReplace {`). Finally, review the code to determine if this class is redundant given Dart's built-in Uri class and consider refactoring to use Uri directly if possible.

Comment on lines 1 to 34
part of '../../parse_server_sdk.dart';

class ParseAggregate {
final String className;
Map<String, dynamic> pipeline;
final bool? debug;
final ParseClient? client;
final bool? autoSendSessionId;
final String? parseClassName;

ParseAggregate(this.className,{required this.pipeline,this.debug, this.client, this.autoSendSessionId, this.parseClassName});

Future<ParseResponse> execute() async {
Map<String,String> _pipeline={};
if(pipeline.isEmpty){
throw ArgumentError('pipeline must not be empty. Please add pipeline operations to aggregate data. Example: {"\$group": {"_id": "\$userId", "totalScore": {"\$sum": "\$score"}}} ');
}
else{
_pipeline.addAll({'pipeline':jsonEncode([pipeline])});
}
final debugBool = isDebugEnabled(objectLevelDebug: debug);
final result = await ParseObject(className)._client.get(
Uri.parse('${ParseCoreData().serverUrl}$keyEndPointAggregate$className').replace(queryParameters: _pipeline).toString(),
);
print('result >>> ${result.data}');
return handleResponse<ParseObject>(
ParseObject(className),
result,
ParseApiRQ.get,
debugBool,
parseClassName ?? 'ParseBase',
);
}
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

❓ Verification inconclusive

Add test coverage for the new aggregate functionality.

The PR objectives mention that tests are planned but not yet implemented. Tests are essential for ensuring this new functionality works correctly.

Please ensure comprehensive test coverage is added for the ParseAggregate class, including tests for:

  1. Successful aggregation queries with various pipeline operations
  2. Error handling for empty pipelines
  3. Error handling for server-side errors
  4. Custom client and debug flag handling

🏁 Script executed:

#!/bin/bash
# Look for test files related to the aggregate functionality
fd -e dart "aggregate.*test" 

# Check coverage in existing test files for references to ParseAggregate
rg -l "ParseAggregate" --type dart packages/dart/test/

Length of output: 85


Add comprehensive test coverage for ParseAggregate

No existing tests for the new aggregate functionality were found. Please add tests in packages/dart/test/ covering:

  • Successful aggregation with various pipeline operations (e.g. $group, $match, $sort, etc.)
  • Error thrown when pipeline is empty
  • Handling of server-side errors (simulate non-200 responses)
  • Custom client injection and debug flag behavior

Ensure these tests exercise both positive and negative scenarios and validate that execute() builds the correct request URI, encodes the pipeline properly, and invokes handleResponse as expected.

🤖 Prompt for AI Agents (early access)
In packages/dart/lib/src/network/parse_aggregate.dart lines 1 to 34, there is no test coverage for the ParseAggregate class. Add comprehensive tests in packages/dart/test/ that cover successful aggregation queries with different pipeline operations like $group, $match, and $sort; verify that an ArgumentError is thrown when the pipeline is empty; simulate server-side errors to test error handling; and test the behavior when a custom client and debug flag are provided. Ensure tests validate the correct request URI construction, proper JSON encoding of the pipeline, and that handleResponse is called correctly.

Comment on lines 13 to 33
Future<ParseResponse> execute() async {
Map<String,String> _pipeline={};
if(pipeline.isEmpty){
throw ArgumentError('pipeline must not be empty. Please add pipeline operations to aggregate data. Example: {"\$group": {"_id": "\$userId", "totalScore": {"\$sum": "\$score"}}} ');
}
else{
_pipeline.addAll({'pipeline':jsonEncode([pipeline])});
}
final debugBool = isDebugEnabled(objectLevelDebug: debug);
final result = await ParseObject(className)._client.get(
Uri.parse('${ParseCoreData().serverUrl}$keyEndPointAggregate$className').replace(queryParameters: _pipeline).toString(),
);
print('result >>> ${result.data}');
return handleResponse<ParseObject>(
ParseObject(className),
result,
ParseApiRQ.get,
debugBool,
parseClassName ?? 'ParseBase',
);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove debug print statement and improve pipeline parameter handling.

The execute() method contains a debug print statement and has some issues with variable naming and error handling.

Make the following improvements:

+  /// Executes the aggregate query and returns the response.
+  /// 
+  /// Throws an [ArgumentError] if the pipeline is empty.
+  /// Returns a [ParseResponse] containing the query results.
  Future<ParseResponse> execute() async {
-    Map<String,String> _pipeline={};
+    Map<String, String> queryParams = {};
    if(pipeline.isEmpty){
      throw ArgumentError('pipeline must not be empty. Please add pipeline operations to aggregate data.  Example: {"\$group": {"_id": "\$userId", "totalScore": {"\$sum": "\$score"}}}  ');
    }
    else{
-      _pipeline.addAll({'pipeline':jsonEncode([pipeline])});
+      queryParams.addAll({'pipeline': jsonEncode([pipeline])});
    }
    final debugBool = isDebugEnabled(objectLevelDebug: debug);
    final result = await ParseObject(className)._client.get(
-      Uri.parse('${ParseCoreData().serverUrl}$keyEndPointAggregate$className').replace(queryParameters: _pipeline).toString(),
+      Uri.parse('${ParseCoreData().serverUrl}$keyEndPointAggregate$className')
+          .replace(queryParameters: queryParams)
+          .toString(),
    );
-    print('result >>> ${result.data}');
+    // Use ParseCoreData().logger instead of print for debugging
+    if (debugBool) {
+      ParseCoreData().logger.info('Aggregate query result: ${result.data}');
+    }
    return handleResponse<ParseObject>(
      ParseObject(className),
      result,
      ParseApiRQ.get,
      debugBool,
      parseClassName ?? 'ParseBase',
    );
  }
📝 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
Future<ParseResponse> execute() async {
Map<String,String> _pipeline={};
if(pipeline.isEmpty){
throw ArgumentError('pipeline must not be empty. Please add pipeline operations to aggregate data. Example: {"\$group": {"_id": "\$userId", "totalScore": {"\$sum": "\$score"}}} ');
}
else{
_pipeline.addAll({'pipeline':jsonEncode([pipeline])});
}
final debugBool = isDebugEnabled(objectLevelDebug: debug);
final result = await ParseObject(className)._client.get(
Uri.parse('${ParseCoreData().serverUrl}$keyEndPointAggregate$className').replace(queryParameters: _pipeline).toString(),
);
print('result >>> ${result.data}');
return handleResponse<ParseObject>(
ParseObject(className),
result,
ParseApiRQ.get,
debugBool,
parseClassName ?? 'ParseBase',
);
}
/// Executes the aggregate query and returns the response.
///
/// Throws an [ArgumentError] if the pipeline is empty.
/// Returns a [ParseResponse] containing the query results.
Future<ParseResponse> execute() async {
Map<String, String> queryParams = {};
if (pipeline.isEmpty) {
throw ArgumentError(
'pipeline must not be empty. Please add pipeline operations to '
'aggregate data. Example: {"\$group": {"_id": "\$userId", '
'"totalScore": {"\$sum": "\$score"}}}',
);
} else {
queryParams.addAll({'pipeline': jsonEncode([pipeline])});
}
final debugBool = isDebugEnabled(objectLevelDebug: debug);
final result = await ParseObject(className)._client.get(
Uri.parse('${ParseCoreData().serverUrl}$keyEndPointAggregate$className')
.replace(queryParameters: queryParams)
.toString(),
);
// Use ParseCoreData().logger instead of print for debugging
if (debugBool) {
ParseCoreData().logger.info('Aggregate query result: ${result.data}');
}
return handleResponse<ParseObject>(
ParseObject(className),
result,
ParseApiRQ.get,
debugBool,
parseClassName ?? 'ParseBase',
);
}
🤖 Prompt for AI Agents (early access)
In packages/dart/lib/src/network/parse_aggregate.dart around lines 13 to 33, remove the debug print statement that outputs the result data. Rename the local variable _pipeline to a more descriptive name like queryParameters. Improve error handling by ensuring the pipeline parameter is properly validated and encoded before adding it to the query parameters. Make sure the pipeline is encoded as a JSON array and passed correctly in the request URI.

@mskayali mskayali mentioned this pull request Apr 30, 2025
mime: ^1.0.0
timezone: ^0.9.4
mime: ^2.0.0
timezone: ^0.10.1
Copy link
Member

Choose a reason for hiding this comment

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

Are these dependency upgrades necessary for this feature?

Copy link
Author

Choose a reason for hiding this comment

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

No but your package dependancies are old and I upgrade it to be compatible with new versions

Copy link
Member

Choose a reason for hiding this comment

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

Coyld you please remove any changes that are not necessary for this PR? You could open another, separate PR for the dependency upgrades, that should only contain the upgrades.

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

🧹 Nitpick comments (2)
packages/dart/lib/src/objects/parse_aggregate.dart (1)

25-25: Consider adding parameter validation.

The method accepts any Map but aggregate pipelines have specific structure requirements. Consider adding validation for common pipeline stages.

 Future<ParseResponse> execute(Map<String, dynamic> pipeline, {Map<String, String>? headers}) async {
+   // Validate pipeline structure
+   final validStages = {'\$match', '\$group', '\$sort', '\$limit', '\$skip', '\$project', '\$unwind'};
+   if (!pipeline.keys.any((key) => validStages.contains(key))) {
+     throw ArgumentError('Pipeline must contain at least one valid aggregation stage: ${validStages.join(', ')}');
+   }
packages/dart/lib/src/objects/parse_jobs.dart (1)

7-58: Consider refactoring to reduce code duplication.

ParseJobs has nearly identical implementation to ParseCloudFunction - only the endpoint differs. Consider extracting common functionality to a base class or mixin to follow DRY principles.

Potential approach:

abstract class ParseCloudBase extends ParseObject {
  final String functionName;
  
  ParseCloudBase(this.functionName, String endpoint, {/* params */}) : super(/*...*/) {
    _path = '$endpoint$functionName';
  }
  
  // Common execute methods here
}

class ParseJobs extends ParseCloudBase {
  ParseJobs(String functionName, {/* params */}) 
    : super(functionName, keyEndPointJobs, /* params */);
}

class ParseCloudFunction extends ParseCloudBase {
  ParseCloudFunction(String functionName, {/* params */}) 
    : super(functionName, keyEndPointFunctions, /* params */);
}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 953b54a and a255b0a.

📒 Files selected for processing (7)
  • packages/dart/example/main.dart (1 hunks)
  • packages/dart/lib/parse_server_sdk.dart (1 hunks)
  • packages/dart/lib/src/base/parse_constants.dart (1 hunks)
  • packages/dart/lib/src/objects/parse_aggregate.dart (1 hunks)
  • packages/dart/lib/src/objects/parse_function.dart (1 hunks)
  • packages/dart/lib/src/objects/parse_jobs.dart (1 hunks)
  • packages/dart/lib/src/objects/parse_response.dart (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/dart/lib/src/base/parse_constants.dart
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/dart/lib/parse_server_sdk.dart
  • packages/dart/example/main.dart
🔇 Additional comments (2)
packages/dart/lib/src/objects/parse_function.dart (1)

18-18: LGTM! Good refactoring to use centralized constants.

Using keyEndPointFunctions instead of hardcoded strings improves maintainability and consistency across the codebase.

packages/dart/lib/src/objects/parse_aggregate.dart (1)

17-17: Endpoint constant is correctly used

Verified that keyEndPointAggregate is defined as /aggregate/ in parse_constants.dart and is consistently used in ParseAggregate (and ParseObject). No changes required here.

Comment on lines +91 to +93
}else if(data.containsKey('error')){
result.error=ParseError(code:response.statusCode, message:data['error'].toString() );
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix formatting and improve error handling.

Missing space after else if and the error handling logic could be improved.

-        }else if(data.containsKey('error')){
-          result.error=ParseError(code:response.statusCode, message:data['error'].toString() );
-        }
+        } else if (data.containsKey('error')) {
+          result.error = ParseError(code: response.statusCode, message: data['error'].toString());
+        }
📝 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
}else if(data.containsKey('error')){
result.error=ParseError(code:response.statusCode, message:data['error'].toString() );
}
} else if (data.containsKey('error')) {
result.error = ParseError(code: response.statusCode, message: data['error'].toString());
}
🤖 Prompt for AI Agents
In packages/dart/lib/src/objects/parse_response.dart around lines 91 to 93, add
a space after `else if` to fix formatting and enhance error handling by ensuring
the error message is safely extracted and properly assigned to result.error,
possibly adding checks or more descriptive handling if needed.

result.results = data;
result.count = data.length;
}
result.results ??= [data];
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Review the fallback logic for results.

The line result.results ??= [data]; might wrap non-list responses unnecessarily. Consider if this is the intended behavior for all response types.


🏁 Script executed:

#!/bin/bash
# Description: Check how Parse Server aggregate endpoint responses are structured
rg -A 5 -B 5 "aggregate.*response|results.*aggregate"

Length of output: 55


🏁 Script executed:

#!/bin/bash
# Display the contents around the fallback logic to inspect the surrounding context
sed -n '1,200p' packages/dart/lib/src/objects/parse_response.dart

Length of output: 3713


Ensure fallback results wrap only on successful, non-error responses

The final line in
packages/dart/lib/src/objects/parse_response.dart:98

result.results ??= [data];

currently applies to every decoded payload—even error objects—so you end up with results containing an error map. To keep results true to its purpose:

  • Only wrap into a list when the request succeeded (response.statusCode in 200–299) and no results were already set.
  • Update count to 1 when you wrap a single object.

Suggested change:

   else if (data is List) {
     result.results = data;
     result.count = data.length;
   }
-  result.results ??= [data];
+  // Fallback: wrap a single successful result
+  if (result.success && result.results == null) {
+    result.results = [data];
+    result.count = 1;
+  }

This ensures error responses aren’t misrepresented in results and keeps count in sync.

🤖 Prompt for AI Agents
In packages/dart/lib/src/objects/parse_response.dart at line 98, the assignment
of result.results to [data] happens regardless of response success, causing
error responses to be incorrectly wrapped in results. Modify the code to only
assign result.results = [data] if the response statusCode is between 200 and 299
and result.results is null. Additionally, set result.count to 1 when wrapping
the single object to keep count accurate.

Comment on lines +99 to +101
} catch (e,s) {
result.error = ParseError(message: e.toString(),exception: Exception(s));
}
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 exception handling in JSON decoding.

The exception handling could provide more specific error information and the stack trace handling looks incomplete.

-    } catch (e,s) {
-      result.error = ParseError(message: e.toString(),exception: Exception(s));
+    } catch (e, stackTrace) {
+      result.error = ParseError(
+        code: response.statusCode,
+        message: 'Failed to parse response: ${e.toString()}',
+        exception: Exception('JSON decode error: $e\nStack trace: $stackTrace')
+      );
📝 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
} catch (e,s) {
result.error = ParseError(message: e.toString(),exception: Exception(s));
}
} catch (e, stackTrace) {
result.error = ParseError(
code: response.statusCode,
message: 'Failed to parse response: ${e.toString()}',
exception: Exception(
'JSON decode error: $e\nStack trace: $stackTrace'
),
);
}
🤖 Prompt for AI Agents
In packages/dart/lib/src/objects/parse_response.dart around lines 99 to 101,
improve the exception handling by capturing more specific error information
during JSON decoding. Instead of wrapping the stack trace string in a generic
Exception, store the actual stack trace object or use a more appropriate type to
preserve detailed debugging information. Adjust the ParseError construction to
include both the error message and the full stack trace for better error
diagnostics.

@mskayali
Copy link
Author

mskayali commented Jul 1, 2025

I've complete the updates

@mtrezza
Copy link
Member

mtrezza commented Nov 23, 2025

@mskayali Could you please comment on each of the open review feedbacks - if you make a commit to address them, they will be automatically closed by coderabbit; if you want to dispute them you can just start a discussion there.

@mtrezza mtrezza changed the title Add Aggregate Query Support to Parse Flutter SDK feat: Add Aggregate Query Support to Parse Flutter SDK Dec 1, 2025
@mtrezza mtrezza changed the title feat: Add Aggregate Query Support to Parse Flutter SDK feat: Add aggregate query support Dec 1, 2025
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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/dart/lib/src/network/parse_websocket_html.dart (1)

56-58: HtmlWebSocketChannel cannot accept IO.Socket from socket_io_client.

HtmlWebSocketChannel is designed to wrap dart:html.WebSocket objects in browser applications. The current implementation passes IO.Socket (from socket_io_client), which is incompatible.

This fundamentally contradicts the design pattern shown in the parallel parse_websocket_io.dart file, which correctly uses io.WebSocket with IOWebSocketChannel. The HTML version should use dart:html.WebSocket instead of importing and using socket_io_client.

🧹 Nitpick comments (1)
packages/dart/lib/src/network/parse_websocket_html.dart (1)

21-26: Improve formatting and consider making the map const.

The map has inconsistent formatting (missing spaces after colons). Consider making it const for better performance since it's immutable.

🔎 Suggested formatting improvement
-  static final Map<String, int> _states={
-    'closed':3,
-    'opening':0,
-    'open':1,
-    'closing':2
+  static const Map<String, int> _states = {
+    'closed': 3,
+    'opening': 0,
+    'open': 1,
+    'closing': 2,
   };
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a255b0a and 18ea019.

📒 Files selected for processing (2)
  • packages/dart/lib/src/network/parse_websocket_html.dart
  • packages/dart/pubspec.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/dart/pubspec.yaml


import 'dart:async';

import 'package:socket_io_client/socket_io_client.dart' as IO;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

socket_io_client dart package latest version 2025 security

💡 Result:

  • Latest stable socket_io_client (Dart) on pub.dev: 3.1.3 (published recently). [1]

  • Known/security notes:

    • No public CVE listing specifically for the Dart socket_io_client package was found; pub.dev shows current package metadata but does not list vulnerabilities. Still check the package repo/issues before use. [1]
    • There are known Socket.IO ecosystem vulnerabilities you should consider when using any client/server combination: CVE-2024-38355 (uncaught error packets can crash Node.js Socket.IO servers; upgrade server-side socket.io to fixed versions). [2]
    • Separate advisory for python-socketio (CVE-2025-61765) describes RCE via pickle deserialization in multi-server setups using an untrusted message queue — an example that server-side implementations can introduce platform-specific risks. [3]
  • Practical recommendations:

    • Use socket_io_client 3.1.3 and keep it updated. [1]
    • Keep your Socket.IO server implementation patched to the latest secure release (follow the server project's SECURITY.md). [2]
    • Use TLS (wss), authenticate/authorize connections, validate and sandbox incoming messages, and avoid trusting inter-server message queues (or use safe serialization). [2][3]
    • Monitor the package repo and vulnerability databases (GitHub Advisories, Snyk, CVE feeds) for new issues.

Sources:
[1] pub.dev — socket_io_client package page and versions.
[2] CVE-2024-38355 / Snyk / CVE details (Socket.IO uncaught error crash).
[3] GitHub Advisory / CVE-2025-61765 (python-socketio pickle deserialization advisory).


🌐 Web query:

socket_io_client pub.dev security vulnerabilities advisories

💡 Result:

Known/security advisories affecting Socket.IO clients (summary):

  • CVE-2024-38355 — Unhandled "error" event can crash a Socket.IO server (DoS). Fixes shipped in socket.io 4.6.2 (and backported to 2.x). (wiz.io)
  • CVE-2022-25867 — io.socket:socket.io-client (Java) null-pointer dereference when parsing malformed packets; fixed in socket.io-client 2.0.1. (osv.dev)
  • CVE-2025-61765 — python-socketio: unsafe pickle deserialization in multi‑server setups (RCE) when attacker has access to the message queue; fixed in python-socketio ≥5.14.0. (nvd.nist.gov)

Recommendations: upgrade client/server packages to the patched versions above, validate/limit inputs, avoid trusting untrusted message-queue payloads (or use safe serializers), and monitor the Socket.IO GitHub security advisories for new issues. (github.com)

Citations:


Update socket_io_client to version 3.1.3.

The imported socket_io_client package is at v3.1.2; version 3.1.3 is available and should be used to ensure the latest stability and bug fixes. No public vulnerabilities specific to the Dart package have been identified, though general Socket.IO ecosystem best practices (e.g., keep the server implementation patched) should be followed.

🤖 Prompt for AI Agents
In packages/dart/lib/src/network/parse_websocket_html.dart around line 7, the
project currently pulls socket_io_client v3.1.2; update the dependency to 3.1.3
by changing the socket_io_client entry in pubspec.yaml to "socket_io_client:
^3.1.3" (or exact 3.1.3 as your versioning policy requires), run flutter pub get
/ dart pub get, and verify there are no new import or API changes required by
running the tests or building the package.

Comment on lines 27 to 48
static Future<WebSocket> connect(String liveQueryURL) async {
final html.WebSocket webSocket = html.WebSocket(liveQueryURL);
await webSocket.onOpen.first;
return WebSocket._(webSocket);
Completer<WebSocket> completer= Completer();
final IO.Socket webSocket = IO.io(
liveQueryURL,
IO.OptionBuilder().setTransports(['websocket']).enableReconnection().build()
);
webSocket.connect();
webSocket.onConnect((handler){
if(!completer.isCompleted){
completer.complete(WebSocket._(webSocket));
}
});
webSocket.onConnectError((handler){
if(!completer.isCompleted){
completer.completeError('unable to connect to the server $handler');
}
});



return completer.future;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Address multiple issues in the connect method.

Several improvements needed:

  1. Line 28: Missing space before the = operator (style inconsistency).
  2. Line 41: The error handler completes with a string instead of a proper Exception or Error type.
  3. No timeout: If neither onConnect nor onConnectError fires, the returned future will hang indefinitely. Consider adding a timeout.
  4. Lines 45-46: Unnecessary blank lines before the return statement.
🔎 Proposed improvements
  static Future<WebSocket> connect(String liveQueryURL) async {
-    Completer<WebSocket> completer= Completer();
+    final Completer<WebSocket> completer = Completer();
    final IO.Socket webSocket = IO.io(
      liveQueryURL,
      IO.OptionBuilder().setTransports(['websocket']).enableReconnection().build()
    );
    webSocket.connect();
-    webSocket.onConnect((handler){
-      if(!completer.isCompleted){
+    webSocket.onConnect((handler) {
+      if (!completer.isCompleted) {
        completer.complete(WebSocket._(webSocket));
      }
    });
-    webSocket.onConnectError((handler){
-      if(!completer.isCompleted){
-        completer.completeError('unable to connect to the server $handler');
+    webSocket.onConnectError((handler) {
+      if (!completer.isCompleted) {
+        completer.completeError(
+          Exception('Unable to connect to the server: $handler')
+        );
      }
    });

-    
-
-    return completer.future;
+    return completer.future.timeout(
+      const Duration(seconds: 30),
+      onTimeout: () {
+        webSocket.disconnect();
+        throw TimeoutException('WebSocket connection timeout');
+      },
+    );
  }
📝 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
static Future<WebSocket> connect(String liveQueryURL) async {
final html.WebSocket webSocket = html.WebSocket(liveQueryURL);
await webSocket.onOpen.first;
return WebSocket._(webSocket);
Completer<WebSocket> completer= Completer();
final IO.Socket webSocket = IO.io(
liveQueryURL,
IO.OptionBuilder().setTransports(['websocket']).enableReconnection().build()
);
webSocket.connect();
webSocket.onConnect((handler){
if(!completer.isCompleted){
completer.complete(WebSocket._(webSocket));
}
});
webSocket.onConnectError((handler){
if(!completer.isCompleted){
completer.completeError('unable to connect to the server $handler');
}
});
return completer.future;
}
static Future<WebSocket> connect(String liveQueryURL) async {
final Completer<WebSocket> completer = Completer();
final IO.Socket webSocket = IO.io(
liveQueryURL,
IO.OptionBuilder().setTransports(['websocket']).enableReconnection().build()
);
webSocket.connect();
webSocket.onConnect((handler) {
if (!completer.isCompleted) {
completer.complete(WebSocket._(webSocket));
}
});
webSocket.onConnectError((handler) {
if (!completer.isCompleted) {
completer.completeError(
Exception('Unable to connect to the server: $handler')
);
}
});
return completer.future.timeout(
const Duration(seconds: 30),
onTimeout: () {
webSocket.disconnect();
throw TimeoutException('WebSocket connection timeout');
},
);
}
🤖 Prompt for AI Agents
In packages/dart/lib/src/network/parse_websocket_html.dart around lines 27 to
48, fix style, error handling and hanging-future issues: add a space before the
'=' at the Completer declaration to match style; change completer.completeError
to pass a proper Exception (e.g., throw or completeError(SocketException or
Exception) with the handler message) instead of a raw string; add a timeout
wrapper (or schedule a timer) that completes the completer with a
TimeoutException if neither onConnect nor onConnectError fire within a
reasonable period (and cancel the timer on success/error); and remove the extra
blank lines before the return statement so the method returns completer.future
immediately after handlers are set up.

}

int get readyState => _webSocket.readyState;
int get readyState => _states[_webSocket.io.readyState]!;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n packages/dart/lib/src/network/parse_websocket_html.dart | head -100

Repository: parse-community/Parse-SDK-Flutter

Length of output: 1982


🏁 Script executed:

rg "_states\s*=" packages/dart/lib/src/network/parse_websocket_html.dart -A 10 -B 2

Repository: parse-community/Parse-SDK-Flutter

Length of output: 472


🏁 Script executed:

rg "readyState" packages/dart/lib/src/network/parse_websocket_html.dart -B 5 -A 5

Repository: parse-community/Parse-SDK-Flutter

Length of output: 254


🏁 Script executed:

cat -n packages/dart/lib/src/network/parse_websocket_io.dart | head -100

Repository: parse-community/Parse-SDK-Flutter

Length of output: 1097


🏁 Script executed:

rg "readyState" packages/dart/lib/src/network/parse_websocket_io.dart -B 5 -A 5

Repository: parse-community/Parse-SDK-Flutter

Length of output: 321


🏁 Script executed:

fd pubspec.yaml packages/dart | xargs cat | grep -A 5 "socket_io_client"

Repository: parse-community/Parse-SDK-Flutter

Length of output: 166


🏁 Script executed:

rg "io\.readyState" packages/dart -B 3 -A 3

Repository: parse-community/Parse-SDK-Flutter

Length of output: 635


🏁 Script executed:

rg "readyState" packages/dart --type dart -B 2 -A 2

Repository: parse-community/Parse-SDK-Flutter

Length of output: 2388


🏁 Script executed:

fd "pubspec.lock" packages/dart -exec cat {} \; | grep -A 20 "socket_io_client"

Repository: parse-community/Parse-SDK-Flutter

Length of output: 223


🏁 Script executed:

find packages/dart -name "pubspec.lock" -type f -exec head -200 {} \; | grep -A 30 "socket_io_client:"

Repository: parse-community/Parse-SDK-Flutter

Length of output: 59


🏁 Script executed:

rg "socket_io_client" packages/dart --type yaml

Repository: parse-community/Parse-SDK-Flutter

Length of output: 130


🌐 Web query:

socket_io_client dart v3.1.2 readyState possible values

💡 Result:

The readyState values follow Dart/WebSocket constants:

  • 0 = connecting
  • 1 = open (connected)
  • 2 = closing
  • 3 = closed. [1][2]

Sources: socket_io_client (pub.dev v3.1.2) and Dart WebSocket readyState/connecting constant. [1] https://pub.dev/packages/socket_io_client/versions/3.1.2/example [2] https://api.dart.dev/stable/dart-io/WebSocket/connecting-constant.html


🌐 Web query:

socket_io_client dart readyState states documentation

💡 Result:

Short answer — readyState uses the standard WebSocket integer states:

  • 0 = CONNECTING
  • 1 = OPEN (connected)
  • 2 = CLOSING
  • 3 = CLOSED

These are the WebSocket readyState values used by dart:io / dart:html (and therefore what socket_io_client will reflect for the underlying socket). See Dart WebSocket readyState and the socket_io_client package docs. [1][2][3]

References:

  • Dart WebSocket readyState (dart:io / dart:html). [1]
  • socket_io_client package documentation (events and connection behavior). [2]
  • MDN WebSocket.readyState (WebSocket spec mapping 0–3). [3]

[1] https://api.dart.dev/dart-io/WebSocket/readyState.html
[2] https://pub.dev/packages/socket_io_client
[3] https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/readyState


Remove the string-to-int state mapping; io.readyState already returns the integer state constant directly.

The _states map uses string keys, but _webSocket.io.readyState returns an integer (0, 1, 2, or 3 per the WebSocket standard used by socket_io_client v3.1.2). The map lookup will always fail, causing the force unwrap to crash on every access. This contradicts the IO implementation (parse_websocket_io.dart line 23), which correctly returns _webSocket.readyState without mapping.

🔎 Recommended fix
-  int get readyState => _states[_webSocket.io.readyState]!;
+  int get readyState => _webSocket.io.readyState;
📝 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
int get readyState => _states[_webSocket.io.readyState]!;
int get readyState => _webSocket.io.readyState;
🤖 Prompt for AI Agents
In packages/dart/lib/src/network/parse_websocket_html.dart around line 50, the
code looks up _webSocket.io.readyState in a string-keyed _states map which fails
because readyState is already an integer; replace the map lookup with a direct
return of the integer state (i.e., return _webSocket.io.readyState) and remove
or stop using the _states mapping so the getter returns the raw integer state
like parse_websocket_io.dart does.

Comment on lines +52 to 54
Future<IO.Socket> close() async {
return _webSocket.disconnect();
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Breaking API change: close() return type modified.

The return type changed from Future<void> to Future<IO.Socket>, which is a breaking change for existing callers. Unless there's a specific reason to return the disconnected socket, consider maintaining the original signature for backward compatibility.

🔎 Suggested fix to maintain backward compatibility
-  Future<IO.Socket> close() async {
-    return _webSocket.disconnect();
+  Future<void> close() async {
+    _webSocket.disconnect();
   }
📝 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
Future<IO.Socket> close() async {
return _webSocket.disconnect();
}
Future<void> close() async {
_webSocket.disconnect();
}
🤖 Prompt for AI Agents
In packages/dart/lib/src/network/parse_websocket_html.dart around lines 52 to
54, the close() method's signature was changed to return Future<IO.Socket> which
is a breaking API change; revert the method to return Future<void> (or
overload/introduce a new method if you need to expose the socket) and ensure the
implementation still awaits _webSocket.disconnect() but does not return the
socket. Update any internal calls if they relied on the socket return, or add a
new method (e.g., disconnectAndReturnSocket) to preserve the new behavior
without breaking existing callers.

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.

aggregate query

2 participants