diff --git a/packages/dart/CHANGELOG.md b/packages/dart/CHANGELOG.md index 7f22d1aed..a8b86c369 100644 --- a/packages/dart/CHANGELOG.md +++ b/packages/dart/CHANGELOG.md @@ -1,3 +1,9 @@ +## [3.1.6](https://github.com/parse-community/Parse-SDK-Flutter/compare/dart-3.1.5...dart-3.1.6) (2022-12-21) + +### Bug Fixes + +* Add `and`, `nor` operators in QueryBuilder ([#795](https://github.com/parse-community/Parse-SDK-Flutter/issues/795)) + ## [3.1.5](https://github.com/parse-community/Parse-SDK-Flutter/compare/dart-3.1.4...dart-3.1.5) (2022-12-16) ### Bug Fixes diff --git a/packages/dart/README.md b/packages/dart/README.md index 2e0477b21..0be5d7be4 100644 --- a/packages/dart/README.md +++ b/packages/dart/README.md @@ -313,6 +313,8 @@ QueryBuilder mainQuery = QueryBuilder.or( var apiResponse = await mainQuery.query(); ``` +To find objects that match several queries use __QueryBuilder.and__. To find objects that do not match any given query use __QueryBuilder.nor__. + The features available are:- * Equals * Contains diff --git a/packages/dart/lib/src/base/parse_constants.dart b/packages/dart/lib/src/base/parse_constants.dart index 5e3f4a0de..133420012 100644 --- a/packages/dart/lib/src/base/parse_constants.dart +++ b/packages/dart/lib/src/base/parse_constants.dart @@ -1,7 +1,7 @@ part of flutter_parse_sdk; // Library -const String keySdkVersion = '3.1.5'; +const String keySdkVersion = '3.1.6'; const String keyLibraryName = 'Flutter Parse SDK'; // End Points diff --git a/packages/dart/lib/src/network/parse_query.dart b/packages/dart/lib/src/network/parse_query.dart index ba989796e..f25bb7c2c 100644 --- a/packages/dart/lib/src/network/parse_query.dart +++ b/packages/dart/lib/src/network/parse_query.dart @@ -10,7 +10,19 @@ class QueryBuilder { } QueryBuilder.or(this.object, List> list) { - String query = '"\$or":['; + _constructorInitializer(query: '"\$or":[', list: list); + } + + QueryBuilder.and(this.object, List> list) { + _constructorInitializer(query: '"\$and":[', list: list); + } + + QueryBuilder.nor(this.object, List> list) { + _constructorInitializer(query: '"\$nor":[', list: list); + } + + void _constructorInitializer( + {required String query, required List> list}) { for (int i = 0; i < list.length; ++i) { if (i > 0) { query += ','; diff --git a/packages/dart/pubspec.yaml b/packages/dart/pubspec.yaml index 7e04a6e19..e642205d6 100644 --- a/packages/dart/pubspec.yaml +++ b/packages/dart/pubspec.yaml @@ -1,6 +1,6 @@ name: parse_server_sdk description: Dart plugin for Parse Server, (https://parseplatform.org), (https://back4app.com) -version: 3.1.5 +version: 3.1.6 homepage: https://github.com/parse-community/Parse-SDK-Flutter environment: diff --git a/packages/dart/test/parse_query_test.dart b/packages/dart/test/parse_query_test.dart index ba343ef40..e93c2a77c 100644 --- a/packages/dart/test/parse_query_test.dart +++ b/packages/dart/test/parse_query_test.dart @@ -62,5 +62,193 @@ void main() { 'where={"\$relatedTo":{"object":{"__type":"Pointer","className":"Post","objectId":"8TOXdXf3tz"},"key":"likes"}}'); expect(result.query, expectedQuery.query); }); + + test('QueryBuilder.or', () async { + final MockParseClient client = MockParseClient(); + + await Parse().initialize( + 'appId', + 'https://test.parse.com', + debug: true, + // to prevent automatic detection + fileDirectory: 'someDirectory', + // to prevent automatic detection + appName: 'appName', + // to prevent automatic detection + appPackageName: 'somePackageName', + // to prevent automatic detection + appVersion: 'someAppVersion', + ); + + ParseObject user = ParseObject("_User", client: client); + var firstName = QueryBuilder(user) + ..regEx('firstName', "Liam"); + + var lastName = QueryBuilder(user) + ..regEx('lastName', "Johnson"); + + QueryBuilder mainQuery = QueryBuilder.or( + user, + [firstName, lastName], + ); + + when(client.get( + any, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + )).thenAnswer((_) async => ParseNetworkResponse( + statusCode: 200, + data: + "{\"results\": [{\"className\": \"_User\",\"objectId\": \"fqx5BECOME\",\"createdAt\": \"2022-10-25T06:04:47.138Z\",\"updatedAt\": \"2022-10-25T06:05:22.328Z\",\"firstName\": \"Liam1\",\"lastName\": \"Johnson1\"},{\"className\": \"_User\",\"objectId\": \"hAtRRYGrUO\",\"createdAt\": \"2022-01-24T15:53:48.396Z\",\"updatedAt\": \"2022-01-25T05:52:01.701Z\",\"firstName\": \"Liam2\",\"lastName\": \"Johnson2\"}]}")); + + var response = await mainQuery.query(); + + expect(response.results?.first, isA()); + + ParseObject parseObject = response.results?.first; + + expect(parseObject.get("firstName"), "Liam1"); + expect(parseObject.objectId, "fqx5BECOME"); + expect(parseObject.createdAt, DateTime.parse("2022-10-25T06:04:47.138Z")); + expect(parseObject.updatedAt, DateTime.parse("2022-10-25T06:05:22.328Z")); + + final Uri result = Uri.parse(verify(client.get( + captureAny, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + )).captured.single); + + expect(result.path, '/classes/_User'); + + final Uri expectedQuery = Uri( + query: + 'where={"\$or":[{"firstName":{"\$regex":"Liam"}},{"lastName":{"\$regex":"Johnson"}}]}'); + expect(result.query, expectedQuery.query); + }); + + test('QueryBuilder.and', () async { + final MockParseClient client = MockParseClient(); + + await Parse().initialize( + 'appId', + 'https://test.parse.com', + debug: true, + // to prevent automatic detection + fileDirectory: 'someDirectory', + // to prevent automatic detection + appName: 'appName', + // to prevent automatic detection + appPackageName: 'somePackageName', + // to prevent automatic detection + appVersion: 'someAppVersion', + ); + + ParseObject user = ParseObject("_User", client: client); + var firstName = QueryBuilder(user) + ..regEx('firstName', "jak"); + + var lastName = QueryBuilder(user)..regEx('lastName', "jaki"); + + QueryBuilder mainQuery = QueryBuilder.and( + user, + [firstName, lastName], + ); + + when(client.get( + any, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + )).thenAnswer((_) async => ParseNetworkResponse( + statusCode: 200, + data: + "{\"results\": [{\"className\": \"_User\",\"objectId\": \"fqx5BECOME\",\"createdAt\": \"2022-10-25T06:04:47.138Z\",\"updatedAt\": \"2022-10-25T06:05:22.328Z\",\"firstName\": \"jak1\",\"lastName\": \"jaki1\"},{\"className\": \"_User\",\"objectId\": \"hAtRRYGrUO\",\"createdAt\": \"2022-01-24T15:53:48.396Z\",\"updatedAt\": \"2022-01-25T05:52:01.701Z\",\"firstName\": \"jak2\",\"lastName\": \"jaki2\"}]}")); + + var response = await mainQuery.query(); + + expect(response.results?.first, isA()); + + ParseObject parseObject = response.results?.first; + + expect(parseObject.get("firstName"), "jak1"); + expect(parseObject.objectId, "fqx5BECOME"); + expect(parseObject.createdAt, DateTime.parse("2022-10-25T06:04:47.138Z")); + expect(parseObject.updatedAt, DateTime.parse("2022-10-25T06:05:22.328Z")); + + final Uri result = Uri.parse(verify(client.get( + captureAny, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + )).captured.single); + + expect(result.path, '/classes/_User'); + + final Uri expectedQuery = Uri( + query: + 'where={"\$and":[{"firstName":{"\$regex":"jak"}},{"lastName":{"\$regex":"jaki"}}]}'); + expect(result.query, expectedQuery.query); + }); + + test('QueryBuilder.nor', () async { + final MockParseClient client = MockParseClient(); + + await Parse().initialize( + 'appId', + 'https://test.parse.com', + debug: true, + // to prevent automatic detection + fileDirectory: 'someDirectory', + // to prevent automatic detection + appName: 'appName', + // to prevent automatic detection + appPackageName: 'somePackageName', + // to prevent automatic detection + appVersion: 'someAppVersion', + ); + + ParseObject user = ParseObject("_User", client: client); + var firstName = QueryBuilder(user) + ..regEx('firstName', "Oliver"); + + var lastName = QueryBuilder(user) + ..regEx('lastName', "Smith"); + + QueryBuilder mainQuery = QueryBuilder.nor( + user, + [firstName, lastName], + ); + + when(client.get( + any, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + )).thenAnswer((_) async => ParseNetworkResponse( + statusCode: 200, + data: + "{\"results\": [{\"className\": \"_User\",\"objectId\": \"fqx5BECOME\",\"createdAt\": \"2022-10-25T06:04:47.138Z\",\"updatedAt\": \"2022-10-25T06:05:22.328Z\",\"firstName\": \"Oliver1\",\"lastName\": \"Smith1\"},{\"className\": \"_User\",\"objectId\": \"hAtRRYGrUO\",\"createdAt\": \"2022-01-24T15:53:48.396Z\",\"updatedAt\": \"2022-01-25T05:52:01.701Z\",\"firstName\": \"Oliver2\",\"lastName\": \"Smith2\"}]}")); + + var response = await mainQuery.query(); + + expect(response.results?.first, isA()); + + ParseObject parseObject = response.results?.first; + + expect(parseObject.get("firstName"), "Oliver1"); + expect(parseObject.objectId, "fqx5BECOME"); + expect(parseObject.createdAt, DateTime.parse("2022-10-25T06:04:47.138Z")); + expect(parseObject.updatedAt, DateTime.parse("2022-10-25T06:05:22.328Z")); + + final Uri result = Uri.parse(verify(client.get( + captureAny, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + )).captured.single); + + expect(result.path, '/classes/_User'); + + final Uri expectedQuery = Uri( + query: + 'where={"\$nor":[{"firstName":{"\$regex":"Oliver"}},{"lastName":{"\$regex":"Smith"}}]}'); + expect(result.query, expectedQuery.query); + }); }); } diff --git a/packages/flutter/README.md b/packages/flutter/README.md index 563af2194..245c3ee16 100644 --- a/packages/flutter/README.md +++ b/packages/flutter/README.md @@ -351,6 +351,8 @@ QueryBuilder mainQuery = QueryBuilder.or( var apiResponse = await mainQuery.query(); ``` +To find objects that match several queries use __QueryBuilder.and__. To find objects that do not match any given query use __QueryBuilder.nor__. + The features available are:- * Equals * Contains