-
Notifications
You must be signed in to change notification settings - Fork 143
Add time between queries #268
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Walkthrough
Suggested reviewers
Pre-merge checks (2 passed, 1 inconclusive)❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (46)
✅ Files skipped from review due to trivial changes (14)
🚧 Files skipped from review as they are similar to previous changes (31)
🔇 Additional comments (2)
✨ Finishing Touches🧪 Generate unit tests
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (5)
README.md (1)
150-153
: Fix stray '>>' in example.
Example shows '<YOUR_PROJECT_ID>>'; drop the extra '>' to avoid copy/paste errors.-Client client = Client().setProject('<YOUR_PROJECT_ID>>'); +Client client = Client().setProject('<YOUR_PROJECT_ID>');lib/src/client_io.dart (1)
61-61
: Header version aligned; consider centralizing.
To avoid future drift across clients, source the SDK version from a single constant.- 'x-sdk-version': '18.1.0', + 'x-sdk-version': ClientBase.sdkVersion,Add once (example):
// in client_base.dart abstract class ClientBase { static const String sdkVersion = '18.1.0'; // ... }lib/src/client_browser.dart (1)
43-43
: Browser header version aligned; same centralization suggestion.
Keep version single-sourced to prevent mismatches.- 'x-sdk-version': '18.1.0', + 'x-sdk-version': ClientBase.sdkVersion,lib/services/account.dart (1)
29-31
: Fix user-facing typos in docs.Minor text nits visible in generated docs. Suggested fixes:
- /// [/account/verfication](https://appwrite.io/docs/references/cloud/client-web/account#createVerification) + /// [/account/verification](https://appwrite.io/docs/references/cloud/client-web/account#createVerification)- /// List the factors available on the account to be used as a MFA challange. + /// List the factors available on the account to be used as a MFA challenge.- /// generate and show then immediately after user successfully adds their - /// authehticator. + /// generate and show them immediately after user successfully adds their + /// authenticator.- /// An OTP challenge is required to regenreate recovery codes. + /// An OTP challenge is required to regenerate recovery codes.- /// provided user ID has not be registered, a new user will be created. + /// provided user ID has not been registered, a new user will be created.Also applies to: 451-454, 471-474, 536-538, 561-564, 584-586, 608-610, 1336-1339
lib/src/enums/execution_method.dart (1)
9-11
: No exhaustive switch on ExecutionMethod found; adding hEAD is safe.
- Optionally add a unit test asserting
ExecutionMethod.hEAD.toJson() == 'HEAD'
.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (9)
README.md
(1 hunks)lib/query.dart
(2 hunks)lib/services/account.dart
(2 hunks)lib/src/client_browser.dart
(1 hunks)lib/src/client_io.dart
(1 hunks)lib/src/enums/credit_card.dart
(1 hunks)lib/src/enums/execution_method.dart
(1 hunks)pubspec.yaml
(1 hunks)test/query_test.dart
(2 hunks)
🔇 Additional comments (7)
pubspec.yaml (1)
2-2
: Version bump confirmed — SDK headers aligned to 18.1.0
Ensure CHANGELOG.md and the Git release tag reflect this update.README.md (1)
24-24
: Dependency snippet bump LGTM.
Snippet matches pubspec 18.1.0.lib/services/account.dart (1)
893-895
: Align deprecation version or confirm intentional
- In lib/services/account.dart at lines 893 and 981 the messages cite “since 1.6.0” whereas all other @deprecated annotations use “since 1.8.0”. If createSession was indeed deprecated in 1.6.0 leave it as-is; otherwise update to 1.8.0.
- Consider appending the deprecated endpoint path (POST /account/sessions/token) to the message for clearer migration guidance.
lib/query.dart (2)
114-117
: createdBetween(): LGTM.API mirrors createdBefore/After and encodes values as a 2-item list with null attribute as expected.
126-129
: updatedBetween(): LGTM.Consistent with updatedBefore/After and toJson semantics.
test/query_test.dart (2)
282-288
: Test for createdBetween(): LGTM.Covers method, attribute, and values shape.
303-308
: Test for updatedBetween(): LGTM.Matches expected wire format.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
lib/query.dart (1)
114-129
: Housekeeping:
- Update PR description, CHANGELOG, and README to cover both the temporal (createdBetween, updatedBefore, updatedAfter, updatedBetween) and spatial (distance*, intersects, crosses, overlaps, touches) query builders.
- Add tests for updatedBefore, updatedAfter, and all spatial filters (distanceEqual/NotEqual/GreaterThan/LessThan, intersects/notIntersects, crosses/notCrosses, overlaps/notOverlaps, touches/notTouches).
🧹 Nitpick comments (2)
lib/query.dart (2)
114-117
: Temporal createdBetween: good addition; clarify timestamp format and inclusivity in docs.
Add RFC 3339/ISO-8601 guidance and an example. Optionally validate empty strings.- /// Filter resources where document was created between [start] and [end] (inclusive). + /// Filter resources where document was created between [start] and [end] (inclusive). + /// Pass RFC 3339/ISO 8601 timestamps (e.g. '2025-01-01T00:00:00Z'). + /// Example: Query.createdBetween('2025-01-01T00:00:00Z', '2025-01-31T23:59:59Z') static String createdBetween(String start, String end) => Query._('createdBetween', null, [start, end]).toString();
126-129
: Temporal updatedBetween: mirror docs from createdBetween.
Match formatting guidance and example for consistency.- /// Filter resources where document was updated between [start] and [end] (inclusive). + /// Filter resources where document was updated between [start] and [end] (inclusive). + /// Pass RFC 3339/ISO 8601 timestamps (e.g. '2025-01-01T00:00:00Z'). + /// Example: Query.updatedBetween('2025-01-01T00:00:00Z', '2025-01-31T23:59:59Z') static String updatedBetween(String start, String end) => Query._('updatedBetween', null, [start, end]).toString();
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
lib/query.dart
(3 hunks)
🔇 Additional comments (1)
lib/query.dart (1)
178-225
: Add tests for distance builders and confirm backend unit expectations
- I didn’t find any existing tests covering distanceEqual/distanceNotEqual/distanceGreaterThan/distanceLessThan; please add unit tests to ensure correct behavior.
- Verify that the backend API expects parameters in the order [coordinates, distance, meters], with meters=true meaning meters (and meters=false meaning kilometers or another unit).
lib/query.dart
Outdated
/// Filter resources where [attribute] intersects with the given geometry. | ||
static String intersects(String attribute, List<dynamic> values) => | ||
Query._('intersects', attribute, values).toString(); | ||
|
||
/// Filter resources where [attribute] does not intersect with the given geometry. | ||
static String notIntersects(String attribute, List<dynamic> values) => | ||
Query._('notIntersects', attribute, values).toString(); | ||
|
||
/// Filter resources where [attribute] crosses the given geometry. | ||
static String crosses(String attribute, List<dynamic> values) => | ||
Query._('crosses', attribute, values).toString(); | ||
|
||
/// Filter resources where [attribute] does not cross the given geometry. | ||
static String notCrosses(String attribute, List<dynamic> values) => | ||
Query._('notCrosses', attribute, values).toString(); | ||
|
||
/// Filter resources where [attribute] overlaps with the given geometry. | ||
static String overlaps(String attribute, List<dynamic> values) => | ||
Query._('overlaps', attribute, values).toString(); | ||
|
||
/// Filter resources where [attribute] does not overlap with the given geometry. | ||
static String notOverlaps(String attribute, List<dynamic> values) => | ||
Query._('notOverlaps', attribute, values).toString(); | ||
|
||
/// Filter resources where [attribute] touches the given geometry. | ||
static String touches(String attribute, List<dynamic> values) => | ||
Query._('touches', attribute, values).toString(); | ||
|
||
/// Filter resources where [attribute] does not touch the given geometry. | ||
static String notTouches(String attribute, List<dynamic> values) => | ||
Query._('notTouches', attribute, values).toString(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Geometry relation builders: clarify geometry format; optional rename and DRY.
- Document expected geometry (e.g., GeoJSON Polygon/LineString/Point).
- Optional: rename values -> geometry for readability; factor a small helper.
+ // Helper for geometry relation queries.
+ static String _rel(String method, String attribute, List<dynamic> geometry) =>
+ Query._(method, attribute, geometry).toString();
+
- /// Filter resources where [attribute] intersects with the given geometry.
- static String intersects(String attribute, List<dynamic> values) =>
- Query._('intersects', attribute, values).toString();
+ /// Filter resources where [attribute] intersects with the given geometry.
+ /// [geometry] should be a GeoJSON-like structure (e.g., Point, LineString, Polygon).
+ static String intersects(String attribute, List<dynamic> geometry) =>
+ _rel('intersects', attribute, geometry);
- /// Filter resources where [attribute] does not intersect with the given geometry.
- static String notIntersects(String attribute, List<dynamic> values) =>
- Query._('notIntersects', attribute, values).toString();
+ /// Filter resources where [attribute] does not intersect with the given geometry.
+ static String notIntersects(String attribute, List<dynamic> geometry) =>
+ _rel('notIntersects', attribute, geometry);
- /// Filter resources where [attribute] crosses the given geometry.
- static String crosses(String attribute, List<dynamic> values) =>
- Query._('crosses', attribute, values).toString();
+ /// Filter resources where [attribute] crosses the given geometry.
+ static String crosses(String attribute, List<dynamic> geometry) =>
+ _rel('crosses', attribute, geometry);
- /// Filter resources where [attribute] does not cross the given geometry.
- static String notCrosses(String attribute, List<dynamic> values) =>
- Query._('notCrosses', attribute, values).toString();
+ /// Filter resources where [attribute] does not cross the given geometry.
+ static String notCrosses(String attribute, List<dynamic> geometry) =>
+ _rel('notCrosses', attribute, geometry);
- /// Filter resources where [attribute] overlaps with the given geometry.
- static String overlaps(String attribute, List<dynamic> values) =>
- Query._('overlaps', attribute, values).toString();
+ /// Filter resources where [attribute] overlaps with the given geometry.
+ static String overlaps(String attribute, List<dynamic> geometry) =>
+ _rel('overlaps', attribute, geometry);
- /// Filter resources where [attribute] does not overlap with the given geometry.
- static String notOverlaps(String attribute, List<dynamic> values) =>
- Query._('notOverlaps', attribute, values).toString();
+ /// Filter resources where [attribute] does not overlap with the given geometry.
+ static String notOverlaps(String attribute, List<dynamic> geometry) =>
+ _rel('notOverlaps', attribute, geometry);
- /// Filter resources where [attribute] touches the given geometry.
- static String touches(String attribute, List<dynamic> values) =>
- Query._('touches', attribute, values).toString();
+ /// Filter resources where [attribute] touches the given geometry.
+ static String touches(String attribute, List<dynamic> geometry) =>
+ _rel('touches', attribute, geometry);
- /// Filter resources where [attribute] does not touch the given geometry.
- static String notTouches(String attribute, List<dynamic> values) =>
- Query._('notTouches', attribute, values).toString();
+ /// Filter resources where [attribute] does not touch the given geometry.
+ static String notTouches(String attribute, List<dynamic> geometry) =>
+ _rel('notTouches', attribute, geometry);
To confirm API parity and naming:
Add tests similar to existing temporal tests to assert method names and payload shapes for each geometry builder.
🌐 Web query:
Appwrite v19 Query API: do geometry relation filters exist with names: intersects, notIntersects, crosses, notCrosses, overlaps, notOverlaps, touches, notTouches? What is the expected geometry payload shape?
💡 Result:
Short answer: No — Appwrite v19 (the current Query API) does not provide built‑in geometry relation filters named intersects / notIntersects / crosses / notCrosses / overlaps / notOverlaps / touches / notTouches. The official Query docs list standard comparison, string, null and array operators but do not include spatial relation operators. [1]
What exists / roadmap notes: Appwrite team discussions indicate geo queries are considered (examples like Query.geoSphere(...) and Query.geoPolygon(...) were proposed) but these are future ideas rather than implemented query operators. [2][3]
Expected payload shape: because geometry/spatial queries are not implemented in v19 there is no documented, supported geometry payload shape in the official Query API. The only available guidance is community/roadmap examples (e.g. passing a point/radius or polygon coordinates as arguments in the proposed Query.geo* APIs), but these are not official specs. If you need spatial queries today you must implement them yourself (store GeoJSON in a field and evaluate geometry relations in your application or a cloud function) or use a DB that supports spatial queries (PostGIS, MongoDB geo queries, etc.). [2][3]
Sources:
- Appwrite Queries documentation (current Query API operator list). [1]
- Appwrite discussions / roadmap mentioning geo query ideas (Query.geoSphere, Query.geoPolygon) and community threads. [2][3]
Remove unsupported spatial query methods
The Appwrite v19 Query API does not support spatial relation operators (intersects, notIntersects, crosses, notCrosses, overlaps, notOverlaps, touches, notTouches) (appwrite.io). Remove these methods or mark them as experimental until backend support is available.
🤖 Prompt for AI Agents
In lib/query.dart around lines 226-256 the eight spatial relation methods
(intersects, notIntersects, crosses, notCrosses, overlaps, notOverlaps, touches,
notTouches) are not supported by Appwrite v19; remove these static methods from
the Query class and delete or update any usages/tests/docs referencing them. If
you prefer to keep placeholders for API parity instead of removal, annotate each
method with a clear @Deprecated message and make them immediately throw an
UnsupportedError (or similar) with a message pointing to Appwrite v19 lack of
support; also update documentation and tests accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
lib/src/realtime_response.dart (1)
53-55
: hashCode is inconsistent with operator==; compute from key/value pairs.mapEquals does shallow content comparison, but data.hashCode is identity-based. Equal objects can yield different hashes.
Apply:
- @override - int get hashCode => type.hashCode ^ data.hashCode; + @override + int get hashCode { + final entriesHash = Object.hashAllUnordered( + data.entries.map((e) => Object.hash(e.key, e.value)), + ); + return Object.hash(type, entriesHash); + }lib/services/account.dart (1)
28-31
: Fix typos and grammar in user-facing docs.These appear in generated docs; please correct.
- /// [/account/verfication](https://appwrite.io/docs/references/cloud/client-web/account#createVerification) + /// [/account/verification](https://appwrite.io/docs/references/cloud/client-web/account#createVerification) ... - /// List the factors available on the account to be used as a MFA challange. + /// List the factors available on the account to be used as a MFA challenge. ... - /// authehticator. Recovery codes can be used as a MFA verification type in + /// authenticator. Recovery codes can be used as an MFA verification type in ... - /// method. An OTP challenge is required to regenreate recovery codes. + /// method. An OTP challenge is required to regenerate recovery codes. ... - /// first. Use the success and failure arguments to provide a redirect URL's + /// first. Use the success and failure arguments to provide redirect URLs ... - /// back to your app when login is completed. + /// back to your app when login completes. ... - /// provided user ID has not be registered, a new user will be created. Use the + /// provided user ID has not been registered, a new user will be created. Use the ... - /// verify the user email ownership. If confirmed this route will return a 200 + /// verify the user's phone number ownership. If confirmed this route will return a 200Also applies to: 176-178, 213-217, 376-379, 452-459, 496-503, 795-803, 1098-1105, 1148-1153, 1253-1256
♻️ Duplicate comments (2)
lib/query.dart (2)
68-71
: Docs are correct re: inclusive/exclusive behavior.between is inclusive; notBetween excludes boundaries. Your comments match current docs — keep as-is. (appwrite.io)
Also applies to: 94-97
194-224
: Remove geometry relation helpers — not implemented in Appwrite.Operators like intersects/crosses/overlaps/touches are not part of the documented Query API. Shipping them will create runtime “Invalid query: method not valid” errors. (appwrite.io)
Apply:
- /// Filter resources where [attribute] intersects with the given geometry. - static String intersects(String attribute, List<dynamic> values) => - Query._('intersects', attribute, values).toString(); - - /// Filter resources where [attribute] does not intersect with the given geometry. - static String notIntersects(String attribute, List<dynamic> values) => - Query._('notIntersects', attribute, values).toString(); - - /// Filter resources where [attribute] crosses the given geometry. - static String crosses(String attribute, List<dynamic> values) => - Query._('crosses', attribute, values).toString(); - - /// Filter resources where [attribute] does not cross the given geometry. - static String notCrosses(String attribute, List<dynamic> values) => - Query._('notCrosses', attribute, values).toString(); - - /// Filter resources where [attribute] overlaps with the given geometry. - static String overlaps(String attribute, List<dynamic> values) => - Query._('overlaps', attribute, values).toString(); - - /// Filter resources where [attribute] does not overlap with the given geometry. - static String notOverlaps(String attribute, List<dynamic> values) => - Query._('notOverlaps', attribute, values).toString(); - - /// Filter resources where [attribute] touches the given geometry. - static String touches(String attribute, List<dynamic> values) => - Query._('touches', attribute, values).toString(); - - /// Filter resources where [attribute] does not touch the given geometry. - static String notTouches(String attribute, List<dynamic> values) => - Query._('notTouches', attribute, values).toString();
🧹 Nitpick comments (16)
lib/src/realtime_io.dart (2)
35-38
: Rename_websok
→channel
for clarity.Minor readability polish.
- final _websok = IOWebSocketChannel((client as ClientIO).selfSigned + final channel = IOWebSocketChannel((client as ClientIO).selfSigned @@ - return _websok; + return channel;
60-62
: Gate noisy debug log to debug builds only.Avoid emitting “Allow self-signed certificate” in release.
- debugPrint('AppwriteRealtime: Allow self-signed certificate'); - return true; + if (kDebugMode) { + debugPrint('AppwriteRealtime: Allow self-signed certificate'); + } + return true;lib/src/client_io.dart (1)
61-63
: Approve version bump
Confirmed ‘x-sdk-version’: ‘19.0.0’ in client_io.dart, client_browser.dart, README.md, and pubspec.yaml. Extraction of the SDK version into a shared constant is recommended to prevent future drift.lib/src/realtime_response.dart (1)
7-10
: Consider makingdata
unmodifiable to prevent post-hash mutation.Freezing guards against subtle map mutations after insertion into hashed collections.
Apply:
- RealtimeResponse({ - required this.type, - required this.data, - }); + RealtimeResponse({ + required this.type, + required Map<String, dynamic> data, + }) : data = Map.unmodifiable(data);And in copyWith (Lines 17-20):
- return RealtimeResponse( - type: type ?? this.type, - data: data ?? this.data, - ); + return RealtimeResponse( + type: type ?? this.type, + data: data != null ? Map.unmodifiable(data) : this.data, + );Optionally also wrap in fromMap:
data: Map.unmodifiable(Map<String, dynamic>.from(map['data'] ?? {})),
lib/services/teams.dart (2)
274-279
: Type prefs as Map<String, dynamic> for stronger typing.Avoid raw Map to catch type errors at compile time.
-Future<models.Preferences> updatePrefs({required String teamId, required Map prefs}) async { +Future<models.Preferences> updatePrefs({required String teamId, required Map<String, dynamic> prefs}) async {
98-101
: Minor nit: extra space before res.data.Tidy return statements.
- return res.data; + return res.data;Also applies to: 221-224
lib/src/realtime_mixin.dart (3)
40-47
: Guard heartbeat pings when socket is closed.Prevent writes after close by checking
closeCode == null
.- _heartbeatTimer = Timer.periodic(Duration(seconds: 20), (_) { - if (_websok != null) { - _websok!.sink.add(jsonEncode({ + _heartbeatTimer = Timer.periodic(const Duration(seconds: 20), (_) { + final ws = _websok; + if (ws != null && ws.closeCode == null) { + ws.sink.add(jsonEncode({ "type": "ping" })); } });
164-168
: Verify multi-value query encoding for channels.Passing a List as a value in
queryParameters
may not serialize as repeated keys. Consider building the query manually (like in OAuth methods) or usingqueryParametersAll
if supported.- return Uri( + final channels = _channels.toList(); + return Uri( host: uri.host, scheme: uri.scheme, port: uri.port, - queryParameters: { - "project": client.config['project'], - "channels[]": _channels.toList(), - }, + query: [ + 'project=${Uri.encodeComponent(client.config['project'] ?? '')}', + for (final c in channels) 'channels[]=${Uri.encodeComponent(c)}', + ].join('&'), path: uri.path + "/realtime", );Run the test app against a project with multiple channels and confirm the server receives all channels.
205-209
: Error handling uses WebSocket close code constant against API error code.
response.data['code']
may not matchstatus.policyViolation
(1008). Prefer inspecting the WScloseCode
or server-defined error codes.- if (response.data['code'] == status.policyViolation) { + final isPolicyViolation = client.realtimeCloseCode == status.policyViolation + || response.data['code'] == status.policyViolation; // if server aligns + if (isPolicyViolation) { throw AppwriteException(response.data["message"], response.data["code"]);If no aligned code exists, treat all errors with
_retry()
and letonDone
handle close codes.lib/services/account.dart (4)
618-627
: Strongly type prefs Map.Prefer
Map<String, dynamic>
to avoid runtime surprises.-Future<models.User> updatePrefs({required Map prefs}) async { +Future<models.User> updatePrefs({required Map<String, dynamic> prefs}) async {
823-835
: Type query list as List.Prevents dynamic leaks.
- final List query = []; + final List<String> query = [];Also applies to: 1124-1136
843-845
: Confirm callbackUrlScheme usage.
callbackUrlScheme
usually expects a scheme (e.g.,myapp
). Passing the fullsuccess
URL may be wrong. ConsidercallbackUrlScheme: success != null ? Uri.parse(success).scheme : null
.- return client.webAuth(url, callbackUrlScheme: success); + return client.webAuth(url, callbackUrlScheme: success != null ? Uri.parse(success).scheme : null);Please verify on iOS/Android with a real redirect.
Also applies to: 1144-1145
103-106
: Unnecessary content-type on GET/DELETE.Harmless but noisy; consider omitting for non-body requests to reduce header churn.
Also applies to: 279-283, 300-304, 716-720, 980-984, 1021-1025, 1197-1201, 1243-1247, 1265-1269
lib/services/databases.dart (1)
125-131
: Nit: avoid Content-Type on GET/DELETE + stray double-space in return.
- The explicit 'content-type': 'application/json' header on GET/DELETE is unnecessary.
- Minor formatting nit: two spaces in
return res.data;
.Apply this diff:
- final Map<String, String> apiHeaders = { - 'content-type': 'application/json', - }; + final Map<String, String> apiHeaders = { + }; @@ - return res.data; + return res.data;lib/services/tables_db.dart (1)
117-123
: Nit: avoid Content-Type on DELETE + stray double-space in return.- final Map<String, String> apiHeaders = { - 'content-type': 'application/json', - }; + final Map<String, String> apiHeaders = { + }; @@ - return res.data; + return res.data;lib/query.dart (1)
28-35
: Minor consistency: equal vs notEqual wrapping.Optional: pass raw value to notEqual and let toJson normalize, mirroring equal(). Behavior unchanged.
- static String notEqual(String attribute, dynamic value) => - Query._('notEqual', attribute, [value]).toString(); + static String notEqual(String attribute, dynamic value) => + Query._('notEqual', attribute, value).toString();Also applies to: 36-43
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (92)
docs/examples/account/update-prefs.md
(1 hunks)docs/examples/databases/create-document.md
(1 hunks)docs/examples/tablesdb/create-row.md
(1 hunks)lib/appwrite.dart
(1 hunks)lib/client_browser.dart
(1 hunks)lib/client_io.dart
(1 hunks)lib/query.dart
(6 hunks)lib/realtime_browser.dart
(1 hunks)lib/realtime_io.dart
(1 hunks)lib/role.dart
(1 hunks)lib/services/account.dart
(18 hunks)lib/services/avatars.dart
(2 hunks)lib/services/databases.dart
(1 hunks)lib/services/functions.dart
(1 hunks)lib/services/graphql.dart
(2 hunks)lib/services/locale.dart
(3 hunks)lib/services/messaging.dart
(1 hunks)lib/services/storage.dart
(1 hunks)lib/services/tables_db.dart
(1 hunks)lib/services/teams.dart
(3 hunks)lib/src/client_base.dart
(0 hunks)lib/src/client_browser.dart
(1 hunks)lib/src/client_io.dart
(1 hunks)lib/src/client_mixin.dart
(2 hunks)lib/src/cookie_manager.dart
(2 hunks)lib/src/enums.dart
(1 hunks)lib/src/enums/authentication_factor.dart
(1 hunks)lib/src/enums/authenticator_type.dart
(1 hunks)lib/src/enums/browser.dart
(1 hunks)lib/src/enums/credit_card.dart
(1 hunks)lib/src/enums/execution_method.dart
(1 hunks)lib/src/enums/flag.dart
(1 hunks)lib/src/enums/image_format.dart
(1 hunks)lib/src/enums/image_gravity.dart
(1 hunks)lib/src/enums/o_auth_provider.dart
(1 hunks)lib/src/exception.dart
(1 hunks)lib/src/models/algo_argon2.dart
(1 hunks)lib/src/models/algo_bcrypt.dart
(1 hunks)lib/src/models/algo_md5.dart
(1 hunks)lib/src/models/algo_phpass.dart
(1 hunks)lib/src/models/algo_scrypt.dart
(1 hunks)lib/src/models/algo_scrypt_modified.dart
(1 hunks)lib/src/models/algo_sha.dart
(1 hunks)lib/src/models/continent.dart
(1 hunks)lib/src/models/continent_list.dart
(1 hunks)lib/src/models/country.dart
(1 hunks)lib/src/models/country_list.dart
(1 hunks)lib/src/models/currency.dart
(1 hunks)lib/src/models/currency_list.dart
(1 hunks)lib/src/models/document.dart
(1 hunks)lib/src/models/document_list.dart
(1 hunks)lib/src/models/execution.dart
(1 hunks)lib/src/models/execution_list.dart
(1 hunks)lib/src/models/file.dart
(1 hunks)lib/src/models/file_list.dart
(1 hunks)lib/src/models/headers.dart
(1 hunks)lib/src/models/identity.dart
(1 hunks)lib/src/models/identity_list.dart
(1 hunks)lib/src/models/jwt.dart
(1 hunks)lib/src/models/language.dart
(1 hunks)lib/src/models/language_list.dart
(1 hunks)lib/src/models/locale.dart
(1 hunks)lib/src/models/locale_code.dart
(1 hunks)lib/src/models/locale_code_list.dart
(1 hunks)lib/src/models/log.dart
(1 hunks)lib/src/models/log_list.dart
(1 hunks)lib/src/models/membership.dart
(1 hunks)lib/src/models/membership_list.dart
(1 hunks)lib/src/models/mfa_challenge.dart
(1 hunks)lib/src/models/mfa_factors.dart
(1 hunks)lib/src/models/mfa_recovery_codes.dart
(1 hunks)lib/src/models/mfa_type.dart
(1 hunks)lib/src/models/model.dart
(1 hunks)lib/src/models/phone.dart
(1 hunks)lib/src/models/phone_list.dart
(1 hunks)lib/src/models/preferences.dart
(1 hunks)lib/src/models/row.dart
(1 hunks)lib/src/models/row_list.dart
(1 hunks)lib/src/models/session.dart
(1 hunks)lib/src/models/session_list.dart
(1 hunks)lib/src/models/subscriber.dart
(1 hunks)lib/src/models/target.dart
(1 hunks)lib/src/models/team.dart
(1 hunks)lib/src/models/team_list.dart
(1 hunks)lib/src/models/token.dart
(1 hunks)lib/src/models/user.dart
(1 hunks)lib/src/realtime.dart
(2 hunks)lib/src/realtime_io.dart
(3 hunks)lib/src/realtime_message.dart
(1 hunks)lib/src/realtime_mixin.dart
(6 hunks)lib/src/realtime_response.dart
(2 hunks)lib/src/realtime_response_connected.dart
(2 hunks)
💤 Files with no reviewable changes (1)
- lib/src/client_base.dart
✅ Files skipped from review due to trivial changes (76)
- lib/client_io.dart
- lib/appwrite.dart
- lib/src/models/row_list.dart
- lib/src/models/model.dart
- docs/examples/account/update-prefs.md
- lib/src/models/algo_scrypt_modified.dart
- lib/src/enums/image_format.dart
- lib/src/models/algo_md5.dart
- lib/src/models/algo_argon2.dart
- lib/src/models/algo_bcrypt.dart
- lib/src/models/target.dart
- lib/src/realtime_message.dart
- lib/src/models/algo_scrypt.dart
- lib/src/models/team.dart
- lib/src/models/document_list.dart
- lib/src/models/file.dart
- lib/src/models/log.dart
- lib/src/enums/authenticator_type.dart
- lib/src/models/preferences.dart
- lib/src/models/language.dart
- lib/src/models/execution_list.dart
- lib/src/models/membership_list.dart
- lib/src/realtime_response_connected.dart
- lib/client_browser.dart
- lib/src/models/file_list.dart
- lib/src/models/subscriber.dart
- lib/src/enums/authentication_factor.dart
- lib/realtime_io.dart
- lib/src/models/row.dart
- lib/realtime_browser.dart
- lib/src/models/currency.dart
- lib/src/models/algo_phpass.dart
- lib/src/models/locale_code.dart
- lib/src/models/algo_sha.dart
- lib/src/models/currency_list.dart
- lib/src/models/locale.dart
- lib/src/models/language_list.dart
- lib/src/exception.dart
- lib/src/models/locale_code_list.dart
- lib/src/models/document.dart
- lib/src/enums/image_gravity.dart
- lib/services/graphql.dart
- lib/src/models/mfa_challenge.dart
- lib/src/models/identity_list.dart
- lib/src/models/log_list.dart
- lib/src/enums/o_auth_provider.dart
- lib/src/models/phone.dart
- lib/src/models/country.dart
- lib/src/models/country_list.dart
- lib/src/models/user.dart
- lib/src/models/jwt.dart
- lib/src/realtime.dart
- lib/src/models/mfa_type.dart
- lib/src/models/phone_list.dart
- lib/src/models/continent.dart
- lib/src/models/headers.dart
- lib/src/cookie_manager.dart
- lib/src/enums/flag.dart
- lib/src/enums/browser.dart
- lib/services/locale.dart
- lib/src/models/execution.dart
- lib/src/models/mfa_factors.dart
- lib/src/enums.dart
- lib/services/storage.dart
- lib/src/models/membership.dart
- lib/src/models/token.dart
- lib/services/avatars.dart
- lib/src/models/identity.dart
- lib/src/client_mixin.dart
- lib/services/functions.dart
- lib/src/models/session.dart
- lib/src/models/team_list.dart
- lib/role.dart
- lib/src/models/session_list.dart
- lib/services/messaging.dart
- lib/src/models/continent_list.dart
🚧 Files skipped from review as they are similar to previous changes (3)
- lib/src/client_browser.dart
- lib/src/enums/credit_card.dart
- lib/src/enums/execution_method.dart
🧰 Additional context used
🪛 markdownlint-cli2 (0.17.2)
docs/examples/tablesdb/create-row.md
15-15: Bare URL used
(MD034, no-bare-urls)
🔇 Additional comments (13)
lib/src/realtime_io.dart (2)
26-26
: Condensed while-condition is fine.No semantic change; readability is acceptable.
53-54
: Signature OK; ensure callers never pass null.Given the updated typing above, keep this as
Map<String, dynamic>
and pass a non-null map from the caller.docs/examples/tablesdb/create-row.md (1)
13-19
: Example payload improvement LGTM.Realistic data makes the snippet more useful.
docs/examples/databases/create-document.md (1)
13-19
: Example payload improvement LGTM.Same comment as the TablesDB snippet—good enhancement.
lib/src/realtime_response.dart (3)
24-27
: LGTM on toMap formatting.No functional changes.
39-39
: LGTM on fromJson inline constructor.No behavior change.
49-50
: Equality check remains appropriate (shallow).Consistent with the adjusted hashCode proposal.
lib/src/models/mfa_recovery_codes.dart (1)
18-22
: LGTM on toMap.Straightforward serialization.
lib/src/realtime_mixin.dart (1)
96-100
: Starting heartbeat after connection is correct.Good move; avoids pinging before auth completes.
lib/services/databases.dart (2)
11-27
: LGTM on formatting-only refactor.Signatures and deprecation annotations are consistent; runtime behavior appears unchanged.
Also applies to: 33-51, 55-71, 77-94, 98-115, 117-133, 136-153, 156-174
15-21
: Ensure null-valued API parameters are removed before calling the client
client.call serializes the params Map as-is, so you must strip out entries where the value isnull
(e.g. withapiParams.removeWhere((_, v) => v == null)
) on all methods using optional fields (lines 15–21, 59–66, 102–110 in lib/services/databases.dart).lib/services/tables_db.dart (2)
9-23
: LGTM. API paths and shapes match TablesDB REST docs.Endpoints under
/tablesdb/{databaseId}/tables/{tableId}/rows[...]
align with current REST reference. (appwrite.io)Also applies to: 30-47, 51-66, 72-88, 92-108, 111-125, 128-144, 147-163
12-18
: Confirm null filtering ofapiParams
. Ensure any null entries are dropped—either by havingclient.call
strip nulls or by invokingapiParams.removeWhere((k, v) => v == null);before the call. Applies in lib/services/tables_db.dart at lines 12–18, 54–60, and 96–103.
permissions: ["read("any")"], // optional | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix invalid permissions syntax in Dart snippet.
Use typed builders to avoid quoting issues.
Apply:
- permissions: ["read("any")"], // optional
+ permissions: [Permission.read(Role.any())], // optional
📝 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.
permissions: ["read("any")"], // optional | |
); | |
permissions: [Permission.read(Role.any())], // optional | |
); |
🤖 Prompt for AI Agents
In docs/examples/databases/create-document.md around lines 20 to 21, the Dart
snippet uses invalid permissions syntax permissions: ["read("any")"] — replace
the quoted raw string with the Dart typed builder form (use the appropriate
Permission/read builder function or constructor with a plain 'any' argument) so
permissions are constructed as typed objects instead of strings; update the
example to call the Permission builder API consistently with the surrounding
snippet and remove the nested quotes/parentheses error.
permissions: ["read("any")"], // optional | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix invalid permissions syntax in Dart snippet.
The current string quoting will not compile. Use the SDK helpers.
Apply:
- permissions: ["read("any")"], // optional
+ permissions: [Permission.read(Role.any())], // optional
📝 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.
permissions: ["read("any")"], // optional | |
); | |
permissions: [Permission.read(Role.any())], // optional | |
); |
🤖 Prompt for AI Agents
In docs/examples/tablesdb/create-row.md around lines 20-21, the Dart permissions
string uses invalid nested quotes and won't compile; replace the string with the
SDK permission helper (use the Permission.read helper and pass "any" as the
argument) instead of the literal "read("any")" string, and ensure the Dart
snippet imports and uses the SDK Permission helper correctly so the permissions
array contains the helper call rather than an invalid quoted string.
/// Filter resources where document was created between [start] and [end] (inclusive). | ||
static String createdBetween(String start, String end) => | ||
Query._('createdBetween', null, [start, end]).toString(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Fix createdBetween/updatedBetween to use supported operators.
The server doesn’t expose “createdBetween”/“updatedBetween” operators. Use between() on $createdAt/$updatedAt instead to keep inclusive semantics documented in Queries. (appwrite.io)
Apply:
- /// Filter resources where document was created between [start] and [end] (inclusive).
- static String createdBetween(String start, String end) =>
- Query._('createdBetween', null, [start, end]).toString();
+ /// Filter resources where document was created between [start] and [end] (inclusive).
+ static String createdBetween(String start, String end) =>
+ between(r'$createdAt', start, end);
@@
- /// Filter resources where document was updated between [start] and [end] (inclusive).
- static String updatedBetween(String start, String end) =>
- Query._('updatedBetween', null, [start, end]).toString();
+ /// Filter resources where document was updated between [start] and [end] (inclusive).
+ static String updatedBetween(String start, String end) =>
+ between(r'$updatedAt', start, end);
If tests assert the JSON method name equals "createdBetween"/"updatedBetween", update them to expect method "between" and attribute "$createdAt"/"$updatedAt", respectively.
Also applies to: 126-129
🏁 Script executed:
#!/bin/bash
# Search for existing between() method and createdBetween/updatedBetween in lib/query.dart
rg -nP --type=dart 'static String between' -n lib/query.dart || true
rg -nP --type=dart 'between\(' -n lib/query.dart || true
# Locate createdBetween and updatedBetween definitions
rg -nP --type=dart 'createdBetween' -n lib/query.dart || true
rg -nP --type=dart 'updatedBetween' -n lib/query.dart || true
# Inspect Query._ constructor to see supported operators
rg -nP --type=dart 'class Query\s*{' -n lib/query.dart -A5 -B0 || true
rg -nP --type=dart 'factory Query\._' -n lib/query.dart -A3 -B3 || true
Length of output: 945
Use between
operator for createdBetween/updatedBetween filters
- lib/query.dart@115–117: replace
with
Query._('createdBetween', null, [start, end]).toString();
between(r'$createdAt', start, end);
- lib/query.dart@127–129: replace
with
Query._('updatedBetween', null, [start, end]).toString();
between(r'$updatedAt', start, end);
- Update any tests that assert method names
"createdBetween"
/"updatedBetween"
to expect"between"
with attributes"$createdAt"
/"$updatedAt"
.
🤖 Prompt for AI Agents
In lib/query.dart around lines 114–117 and 127–129, the createdBetween and
updatedBetween helpers currently construct Query._ with operator names
"createdBetween"/"updatedBetween"; change them to call between(r'$createdAt',
start, end) and between(r'$updatedAt', start, end) respectively so the operator
used is "between" with the correct attribute; also update any tests that assert
the operator name "createdBetween"/"updatedBetween" to expect "between" and that
the attribute is "$createdAt"/"$updatedAt".
💡 Verification agent
🧩 Analysis chain
Repo-wide verification for API compatibility.
Update tests/usage to match the supported shape; remove any geometry/distance usages.
Also applies to: 126-129, 178-224
🏁 Script executed:
#!/bin/bash
# 1) Find usages of newly added helpers
rg -nP --type=dart -C2 '\b(createdBetween|updatedBetween|distance(Equal|NotEqual|GreaterThan|LessThan)|intersects|notIntersects|crosses|notCrosses|overlaps|notOverlaps|touches|notTouches)\s*\('
# 2) Show current JSON emitted by Query.createdBetween/updatedBetween
rg -nP -C3 'static\s+String\s+createdBetween|static\s+String\s+updatedBetween' lib/query.dart
# 3) Grep tests that assert method name/attribute for created/updated helpers
rg -nP -C2 'createdBetween|updatedBetween' test
Length of output: 7187
Remove unsupported geometry/distance filter methods
Delete the distance* and geometry filter helpers (distanceEqual/NotEqual/GreaterThan/LessThan and intersects/notIntersects/crosses/notCrosses/overlaps/notOverlaps/touches/notTouches) from lib/query.dart (lines 178–224).
🤖 Prompt for AI Agents
In lib/query.dart around lines 178 to 224, remove the unsupported
geometry/distance filter helper methods: all distance* methods (distanceEqual,
distanceNotEqual, distanceGreaterThan, distanceLessThan) and all geometry
intersection/relationship helpers (intersects, notIntersects, crosses,
notCrosses, overlaps, notOverlaps, touches, notTouches). Delete their method
definitions and any associated imports or references that become unused as a
result, and run a quick project build/test to ensure there are no remaining
references to these methods.
lib/query.dart
Outdated
/// Filter resources where [attribute] is at a specific distance from the given coordinates. | ||
static String distanceEqual(String attribute, List<dynamic> values, double distance, [bool meters = true]) => | ||
Query._('distanceEqual', attribute, [values, distance, meters]).toString(); | ||
|
||
/// Filter resources where [attribute] is not at a specific distance from the given coordinates. | ||
static String distanceNotEqual(String attribute, List<dynamic> values, double distance, [bool meters = true]) => | ||
Query._('distanceNotEqual', attribute, [values, distance, meters]).toString(); | ||
|
||
/// Filter resources where [attribute] is at a distance greater than the specified value from the given coordinates. | ||
static String distanceGreaterThan(String attribute, List<dynamic> values, double distance, [bool meters = true]) => | ||
Query._('distanceGreaterThan', attribute, [values, distance, meters]).toString(); | ||
|
||
/// Filter resources where [attribute] is at a distance less than the specified value from the given coordinates. | ||
static String distanceLessThan(String attribute, List<dynamic> values, double distance, [bool meters = true]) => | ||
Query._('distanceLessThan', attribute, [values, distance, meters]).toString(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Remove distance helpers — geospatial distance queries are not supported.*
Appwrite queries don’t support distanceEqual/NotEqual/GreaterThan/LessThan operators today; recommended patterns use bounding-box comparisons on lat/long attributes client-side. Keep these out to avoid shipping unusable APIs. (appwrite.io, dev.to)
Apply:
- /// Filter resources where [attribute] is at a specific distance from the given coordinates.
- static String distanceEqual(String attribute, List<dynamic> values, double distance, [bool meters = true]) =>
- Query._('distanceEqual', attribute, [values, distance, meters]).toString();
-
- /// Filter resources where [attribute] is not at a specific distance from the given coordinates.
- static String distanceNotEqual(String attribute, List<dynamic> values, double distance, [bool meters = true]) =>
- Query._('distanceNotEqual', attribute, [values, distance, meters]).toString();
-
- /// Filter resources where [attribute] is at a distance greater than the specified value from the given coordinates.
- static String distanceGreaterThan(String attribute, List<dynamic> values, double distance, [bool meters = true]) =>
- Query._('distanceGreaterThan', attribute, [values, distance, meters]).toString();
-
- /// Filter resources where [attribute] is at a distance less than the specified value from the given coordinates.
- static String distanceLessThan(String attribute, List<dynamic> values, double distance, [bool meters = true]) =>
- Query._('distanceLessThan', attribute, [values, distance, meters]).toString();
Alternatively, gate them with @deprecated and throw UnsupportedError immediately with a link to docs.
📝 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.
/// Filter resources where [attribute] is at a specific distance from the given coordinates. | |
static String distanceEqual(String attribute, List<dynamic> values, double distance, [bool meters = true]) => | |
Query._('distanceEqual', attribute, [values, distance, meters]).toString(); | |
/// Filter resources where [attribute] is not at a specific distance from the given coordinates. | |
static String distanceNotEqual(String attribute, List<dynamic> values, double distance, [bool meters = true]) => | |
Query._('distanceNotEqual', attribute, [values, distance, meters]).toString(); | |
/// Filter resources where [attribute] is at a distance greater than the specified value from the given coordinates. | |
static String distanceGreaterThan(String attribute, List<dynamic> values, double distance, [bool meters = true]) => | |
Query._('distanceGreaterThan', attribute, [values, distance, meters]).toString(); | |
/// Filter resources where [attribute] is at a distance less than the specified value from the given coordinates. | |
static String distanceLessThan(String attribute, List<dynamic> values, double distance, [bool meters = true]) => | |
Query._('distanceLessThan', attribute, [values, distance, meters]).toString(); |
🤖 Prompt for AI Agents
In lib/query.dart around lines 178 to 193, the distance* helper methods expose
geospatial distance queries which Appwrite does not support; remove these four
methods (distanceEqual, distanceNotEqual, distanceGreaterThan, distanceLessThan)
from the public API OR mark each with @Deprecated and change their
implementation to immediately throw an UnsupportedError with a short message and
a link to the Appwrite docs explaining the limitation (include the docs URL), so
callers get a clear runtime error instead of an unusable API.
lib/services/account.dart
Outdated
final String apiPath = '/account/identities/{identityId}'.replaceAll('{identityId}', identityId); | ||
|
||
final Map<String, dynamic> apiParams = {}; | ||
final Map<String, dynamic> apiParams = { | ||
}; | ||
|
||
final Map<String, String> apiHeaders = {'content-type': 'application/json'}; | ||
final Map<String, String> apiHeaders = { | ||
'content-type': 'application/json', | ||
}; | ||
|
||
final res = await client.call( | ||
HttpMethod.delete, | ||
path: apiPath, | ||
params: apiParams, | ||
headers: apiHeaders, | ||
); | ||
final res = await client.call(HttpMethod.delete, path: apiPath, params: apiParams, headers: apiHeaders); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
URL-encode IDs used in paths.
Use Uri.encodeComponent
to avoid path issues with unexpected characters.
- final String apiPath = '/account/identities/{identityId}'.replaceAll('{identityId}', identityId);
+ final String apiPath = '/account/identities/{identityId}'.replaceAll('{identityId}', Uri.encodeComponent(identityId));
- final String apiPath = '/account/mfa/authenticators/{type}'.replaceAll('{type}', type.value);
+ final String apiPath = '/account/mfa/authenticators/{type}'.replaceAll('{type}', Uri.encodeComponent(type.value));
- final String apiPath = '/account/sessions/{sessionId}'.replaceAll('{sessionId}', sessionId);
+ final String apiPath = '/account/sessions/{sessionId}'.replaceAll('{sessionId}', Uri.encodeComponent(sessionId));
- final String apiPath = '/account/targets/{targetId}/push'.replaceAll('{targetId}', targetId);
+ final String apiPath = '/account/targets/{targetId}/push'.replaceAll('{targetId}', Uri.encodeComponent(targetId));
Apply similarly to all other interpolated path segments in this file.
Also applies to: 272-283, 355-364, 392-399, 520-531, 571-579, 596-603, 692-705, 731-744, 891-903, 910-923, 931-943, 996-1006, 1016-1025, 1211-1223, 1258-1271
🤖 Prompt for AI Agents
In lib/services/account.dart around lines 98 to 107 (and similarly for ranges
272-283, 355-364, 392-399, 520-531, 571-579, 596-603, 692-705, 731-744, 891-903,
910-923, 931-943, 996-1006, 1016-1025, 1211-1223, 1258-1271), the identity/other
IDs interpolated directly into URL paths must be URL-encoded to avoid breaking
the path when they contain reserved characters; update each interpolated segment
to wrap the variable with Uri.encodeComponent(...) when building the apiPath
string so every path parameter is properly encoded before calling client.call.
lib/services/teams.dart
Outdated
final String apiPath = '/teams/{teamId}'.replaceAll('{teamId}', teamId); | ||
|
||
final Map<String, dynamic> apiParams = {}; | ||
final Map<String, dynamic> apiParams = { | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
URL-encode path params to prevent malformed paths and edge-case injection.
Wrap all interpolated IDs with Uri.encodeComponent(...)
when building apiPath
.
- final String apiPath = '/teams/{teamId}'.replaceAll('{teamId}', teamId);
+ final String apiPath = '/teams/{teamId}'.replaceAll('{teamId}', Uri.encodeComponent(teamId));
- final String apiPath = '/teams/{teamId}/memberships'.replaceAll('{teamId}', teamId);
+ final String apiPath = '/teams/{teamId}/memberships'.replaceAll('{teamId}', Uri.encodeComponent(teamId));
- final String apiPath = '/teams/{teamId}/memberships/{membershipId}'.replaceAll('{teamId}', teamId).replaceAll('{membershipId}', membershipId);
+ final String apiPath = '/teams/{teamId}/memberships/{membershipId}'
+ .replaceAll('{teamId}', Uri.encodeComponent(teamId))
+ .replaceAll('{membershipId}', Uri.encodeComponent(membershipId));
- final String apiPath = '/teams/{teamId}/memberships/{membershipId}/status'.replaceAll('{teamId}', teamId).replaceAll('{membershipId}', membershipId);
+ final String apiPath = '/teams/{teamId}/memberships/{membershipId}/status'
+ .replaceAll('{teamId}', Uri.encodeComponent(teamId))
+ .replaceAll('{membershipId}', Uri.encodeComponent(membershipId));
- final String apiPath = '/teams/{teamId}/prefs'.replaceAll('{teamId}', teamId);
+ final String apiPath = '/teams/{teamId}/prefs'.replaceAll('{teamId}', Uri.encodeComponent(teamId));
Also applies to: 70-76, 89-96, 108-110, 171-178, 191-200, 211-219, 235-244, 256-262, 275-283
factory MfaRecoveryCodes.fromMap(Map<String, dynamic> map) { | ||
return MfaRecoveryCodes( | ||
recoveryCodes: List.from(map['recoveryCodes'] ?? []), | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Use typed List conversion to satisfy strong mode and avoid runtime type issues.
Current List.from(...)
yields List<dynamic>
. Prefer generic conversion.
Apply:
- return MfaRecoveryCodes(
- recoveryCodes: List.from(map['recoveryCodes'] ?? []),
- );
+ return MfaRecoveryCodes(
+ recoveryCodes: List<String>.from(map['recoveryCodes'] ?? const <String>[]),
+ );
Optionally make it immutable with UnmodifiableListView
if mutation should be prevented.
📝 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.
factory MfaRecoveryCodes.fromMap(Map<String, dynamic> map) { | |
return MfaRecoveryCodes( | |
recoveryCodes: List.from(map['recoveryCodes'] ?? []), | |
); | |
} | |
factory MfaRecoveryCodes.fromMap(Map<String, dynamic> map) { | |
return MfaRecoveryCodes( | |
recoveryCodes: List<String>.from(map['recoveryCodes'] ?? const <String>[]), | |
); | |
} |
🤖 Prompt for AI Agents
In lib/src/models/mfa_recovery_codes.dart around lines 12 to 16, the factory
uses List.from(map['recoveryCodes'] ?? []) which produces List<dynamic>; change
it to construct a typed list such as List<String>.from(map['recoveryCodes'] ??
const []) to satisfy strong mode and avoid runtime type errors, and if the list
should be immutable wrap it in an UnmodifiableListView before passing to the
constructor.
final _websok = IOWebSocketChannel((client as ClientIO).selfSigned | ||
? await _connectForSelfSignedCert(uri, headers) | ||
: await WebSocket.connect(uri.toString(), headers: headers)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Headers type mismatch can break compilation; make headers Map<String, dynamic>.
WebSocket.connect(..., headers: ...)
(and _connectForSelfSignedCert
) expect Map<String, dynamic>
, but headers
is declared Map<String, String>?
. Depending on analyzer settings, this will error or at least warn; it’s safer to align types and ensure non-null before passing.
Apply this minimal diff:
- Map<String, String>? headers;
+ // Align with WebSocket.connect headers: Map<String, dynamic>
+ Map<String, dynamic>? headers;
@@
- headers = {HttpHeaders.cookieHeader: CookieManager.getCookies(cookies)};
+ headers = {HttpHeaders.cookieHeader: CookieManager.getCookies(cookies)};
@@
- final _websok = IOWebSocketChannel((client as ClientIO).selfSigned
- ? await _connectForSelfSignedCert(uri, headers)
- : await WebSocket.connect(uri.toString(), headers: headers));
+ final _websok = IOWebSocketChannel((client as ClientIO).selfSigned
+ ? await _connectForSelfSignedCert(uri, headers!)
+ : await WebSocket.connect(uri.toString(), headers: headers!));
If you prefer not to change the declaration, cast at call sites instead:
- ? await _connectForSelfSignedCert(uri, headers)
- : await WebSocket.connect(uri.toString(), headers: headers));
+ ? await _connectForSelfSignedCert(uri, headers!.cast<String, dynamic>())
+ : await WebSocket.connect(uri.toString(), headers: headers!.cast<String, dynamic>()));
🏁 Script executed:
#!/bin/bash
set -e
# Locate realtime_io.dart
file=$(find . -type f -name 'realtime_io.dart' | head -n1)
echo "File: $file"
echo "----- Definition of _connectForSelfSignedCert -----"
rg -n "Future<.*_connectForSelfSignedCert" -C3 "$file"
echo "----- Declaration of headers variable -----"
rg -n "Map<String, String>\\? headers" -C1 "$file"
echo "----- Occurrences of headers assignment -----"
rg -n "headers\\s*=" -C1 "$file"
Length of output: 1367
Align headers
type with WebSocket APIs and unwrap before passing
headers
is declared as Map<String, String>?
but both
Future<WebSocket> _connectForSelfSignedCert(Uri, Map<String, dynamic>)
and
WebSocket.connect(..., headers: Map<String, dynamic>?)
expect Map<String, dynamic>
. Change the declaration and update the calls:
- Map<String, String>? headers;
+ Map<String, dynamic>? headers;
@@
- ? await _connectForSelfSignedCert(uri, headers)
- : await WebSocket.connect(uri.toString(), headers: headers));
+ ? await _connectForSelfSignedCert(uri, headers!)
+ : await WebSocket.connect(uri.toString(), headers: headers!));
Or, if you’d rather keep the original declaration, cast at call sites:
- ? await _connectForSelfSignedCert(uri, headers)
- : await WebSocket.connect(uri.toString(), headers: headers));
+ ? await _connectForSelfSignedCert(uri, headers!.cast<String, dynamic>())
+ : await WebSocket.connect(uri.toString(), headers: headers!.cast<String, dynamic>()));
🤖 Prompt for AI Agents
In lib/src/realtime_io.dart around lines 35 to 37, the local variable `headers`
is typed as Map<String, String>? but the WebSocket APIs and
_connectForSelfSignedCert expect Map<String, dynamic>?; fix by making the types
consistent: either change the declaration of `headers` (and the signature of
_connectForSelfSignedCert if defined in this file) to Map<String, dynamic>? so
you can pass it directly, or keep String and explicitly cast/unpack at call
sites (e.g., pass headers as headers as Map<String, dynamic>? or
headers?.cast<String, dynamic>()), ensuring nullability is preserved in both the
WebSocket.connect and _connectForSelfSignedCert calls.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (4)
lib/query.dart (4)
116-119
: Use between on $createdAt instead of introducing unsupported createdBetweenAppwrite doesn’t expose a createdBetween operator; emit between on $createdAt to remain server-compatible.
Apply:
- /// Filter resources where document was created between [start] and [end] (inclusive). - static String createdBetween(String start, String end) => - Query._('createdBetween', null, [start, end]).toString(); + /// Filter resources where document was created between [start] and [end] (inclusive). + static String createdBetween(String start, String end) => + between(r'$createdAt', start, end);Does Appwrite v19 support a "createdBetween" operator, or should clients use between on $createdAt?
128-131
: Use between on $updatedAt instead of unsupported updatedBetweenSame rationale as above; switch to between on $updatedAt.
Apply:
- /// Filter resources where document was updated between [start] and [end] (inclusive). - static String updatedBetween(String start, String end) => - Query._('updatedBetween', null, [start, end]).toString(); + /// Filter resources where document was updated between [start] and [end] (inclusive). + static String updatedBetween(String start, String end) => + between(r'$updatedAt', start, end);Is there an "updatedBetween" operator in Appwrite v19, or should we use between on $updatedAt?
180-195
: Remove distance helpers — not supported; plus nested payload bug*
- Appwrite queries don’t support distanceEqual/NotEqual/GreaterThan/LessThan. Shipping these will mislead users and cause 400s.
- Current payload uses an extra nesting ([[values, distance, meters]]); even if supported, this shape would be wrong.
Prefer removal (or gate behind @deprecated and throw).
Removal:
- /// Filter resources where [attribute] is at a specific distance from the given coordinates. - static String distanceEqual(String attribute, List<dynamic> values, num distance, [bool meters = true]) => - Query._('distanceEqual', attribute, [[values, distance, meters]]).toString(); - - /// Filter resources where [attribute] is not at a specific distance from the given coordinates. - static String distanceNotEqual(String attribute, List<dynamic> values, num distance, [bool meters = true]) => - Query._('distanceNotEqual', attribute, [[values, distance, meters]]).toString(); - - /// Filter resources where [attribute] is at a distance greater than the specified value from the given coordinates. - static String distanceGreaterThan(String attribute, List<dynamic> values, num distance, [bool meters = true]) => - Query._('distanceGreaterThan', attribute, [[values, distance, meters]]).toString(); - - /// Filter resources where [attribute] is at a distance less than the specified value from the given coordinates. - static String distanceLessThan(String attribute, List<dynamic> values, num distance, [bool meters = true]) => - Query._('distanceLessThan', attribute, [[values, distance, meters]]).toString();If you insist on keeping placeholders, deprecate and throw:
- static String distanceEqual(String attribute, List<dynamic> values, num distance, [bool meters = true]) => - Query._('distanceEqual', attribute, [[values, distance, meters]]).toString(); + @Deprecated('Not supported by Appwrite queries (v19).') + static String distanceEqual(String attribute, List<dynamic> values, num distance, [bool meters = true]) => + throw UnsupportedError('distanceEqual is not supported by Appwrite queries.');Do Appwrite v19 database queries support geospatial distance operators?
196-226
: Remove geometry relation helpers — unsupported; also avoid double-wrapping payload and rename param
- intersects/notIntersects/crosses/notCrosses/overlaps/notOverlaps/touches/notTouches aren’t supported in Appwrite queries.
- Current implementation wraps values as [values], producing a nested array; even if supported, shape would be off.
- If ever added, prefer parameter name geometry and pass it directly (not wrapped).
Removal:
- /// Filter resources where [attribute] intersects with the given geometry. - static String intersects(String attribute, List<dynamic> values) => - Query._('intersects', attribute, [values]).toString(); - /// Filter resources where [attribute] does not intersect with the given geometry. - static String notIntersects(String attribute, List<dynamic> values) => - Query._('notIntersects', attribute, [values]).toString(); - /// Filter resources where [attribute] crosses the given geometry. - static String crosses(String attribute, List<dynamic> values) => - Query._('crosses', attribute, [values]).toString(); - /// Filter resources where [attribute] does not cross the given geometry. - static String notCrosses(String attribute, List<dynamic> values) => - Query._('notCrosses', attribute, [values]).toString(); - /// Filter resources where [attribute] overlaps with the given geometry. - static String overlaps(String attribute, List<dynamic> values) => - Query._('overlaps', attribute, [values]).toString(); - /// Filter resources where [attribute] does not overlap with the given geometry. - static String notOverlaps(String attribute, List<dynamic> values) => - Query._('notOverlaps', attribute, [values]).toString(); - /// Filter resources where [attribute] touches the given geometry. - static String touches(String attribute, List<dynamic> values) => - Query._('touches', attribute, [values]).toString(); - /// Filter resources where [attribute] does not touch the given geometry. - static String notTouches(String attribute, List<dynamic> values) => - Query._('notTouches', attribute, [values]).toString();If placeholders are kept:
- static String intersects(String attribute, List<dynamic> values) => - Query._('intersects', attribute, [values]).toString(); + @Deprecated('Not supported by Appwrite queries (v19).') + static String intersects(String attribute, List<dynamic> geometry) => + throw UnsupportedError('intersects is not supported by Appwrite queries.');Are spatial relation operators (intersects, crosses, overlaps, touches) available in Appwrite v19 database queries?
🧹 Nitpick comments (3)
lib/query.dart (3)
156-162
: Doc nit: remove stray blank line to match surrounding styleTiny consistency polish.
164-168
: Doc nit: remove stray blank line to match surrounding styleTiny consistency polish.
174-178
: Doc nit: remove stray blank line to match surrounding styleTiny consistency polish.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
lib/query.dart
(6 hunks)
🔇 Additional comments (2)
lib/query.dart (2)
11-25
: toJson refactor: OK; normalization preservedRenaming to result and keeping "values is List ? values : [values]" preserves the wire shape. No concerns here.
38-41
: notEqual payload tweak keeps behavior unchangedPassing value (not [value]) still serializes to ["value"]; this is fine and consistent with other builders.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing updated CHANGELOG.md
It doesn't seem like the formatting as applied 🧐
# Conflicts: # lib/services/account.dart # lib/services/avatars.dart # lib/services/storage.dart
What does this PR do?
(Provide a description of what this PR does.)
Test Plan
(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.)
Related PRs and Issues
(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)
Have you read the Contributing Guidelines on issues?
(Write your answer here.)
Summary by CodeRabbit