From 8c7f286cd8536082aede79270b6e12210022ac15 Mon Sep 17 00:00:00 2001 From: Phill Date: Sun, 23 Jun 2019 16:39:43 +0100 Subject: [PATCH 01/25] Merged v1.0.22 --- example/lib/domain/constants/application_constants.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/example/lib/domain/constants/application_constants.dart b/example/lib/domain/constants/application_constants.dart index 6b74994a9..01491a0dc 100644 --- a/example/lib/domain/constants/application_constants.dart +++ b/example/lib/domain/constants/application_constants.dart @@ -1,4 +1,4 @@ -const String keyApplicationName = 'BodyCal'; -const String keyParseApplicationId = 'bodycaldb'; -const String keyParseMasterKey = '343gf35g4t6hev445f4t5f45g45d'; -const String keyParseServerUrl = 'http://45.76.129.16:1338/parse/'; +const String keyApplicationName = ''; +const String keyParseApplicationId = ''; +const String keyParseMasterKey = ''; +const String keyParseServerUrl = ''; From ea77ae690259caee3e1bc2f6a5bae93d1504420f Mon Sep 17 00:00:00 2001 From: Phill Date: Sun, 23 Jun 2019 16:42:54 +0100 Subject: [PATCH 02/25] Updated versioning --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/src/base/parse_constants.dart | 2 +- pubspec.yaml | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eef1ece34..df00f55ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ +## 1.0.23 + ## 1.0.22 +Added dirty children +Added option of sembast or share_preferences ## 1.0.21 LiveQuery fix diff --git a/README.md b/README.md index 8c6cd9020..2fd12bdc6 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Want to get involved? Join our Slack channel and help out! (http://flutter-parse To install, either add to your pubspec.yaml ```yml dependencies: - parse_server_sdk: ^1.0.22 + parse_server_sdk: ^1.0.23 ``` or clone this repository and add to your project. As this is an early development with multiple contributors, it is probably best to download/clone and keep updating as an when a new feature is added. diff --git a/lib/src/base/parse_constants.dart b/lib/src/base/parse_constants.dart index 2f61a8c05..0fbac1854 100644 --- a/lib/src/base/parse_constants.dart +++ b/lib/src/base/parse_constants.dart @@ -1,7 +1,7 @@ part of flutter_parse_sdk; // Library -const String keySdkVersion = '1.0.22'; +const String keySdkVersion = '1.0.23'; const String keyLibraryName = 'Flutter Parse SDK'; // End Points diff --git a/pubspec.yaml b/pubspec.yaml index 1223c4559..b6a924a74 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: parse_server_sdk description: Flutter plugin for Parse Server, (https://parseplatform.org), (https://back4app.com) -version: 1.0.22 +version: 1.0.23 homepage: https://github.com/phillwiggins/flutter_parse_sdk author: PhillWiggins From 3bf90476463a38c014878d8ac189ae6eadde4415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E8=90=A7=E7=8E=89?= Date: Tue, 2 Jul 2019 13:10:26 +0800 Subject: [PATCH 03/25] Hot-fix (#209) * Merged v1.0.22 * 1. Fix return result from save method. This is important. 2. Fix unsavedChanges issue in ParseUser login and signUp method. 3. Delete useless and reduplicative decode in ParseUser. Because this is already done in handleResponse method. This also cause wrong unsaved changes. --- lib/src/objects/parse_object.dart | 1 + lib/src/objects/parse_user.dart | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/src/objects/parse_object.dart b/lib/src/objects/parse_object.dart index d23d6127a..2c790c2c8 100644 --- a/lib/src/objects/parse_object.dart +++ b/lib/src/objects/parse_object.dart @@ -108,6 +108,7 @@ class ParseObject extends ParseBase implements ParseCloneable { else { _revertSavingChanges(); } + return response; } } return childrenResponse; diff --git a/lib/src/objects/parse_user.dart b/lib/src/objects/parse_user.dart index 3da6d4496..f2b99e2f5 100644 --- a/lib/src/objects/parse_user.dart +++ b/lib/src/objects/parse_user.dart @@ -136,11 +136,13 @@ class ParseUser extends ParseObject implements ParseCloneable { bodyData[keyVarPassword] = password; bodyData[keyVarUsername] = username; final Uri url = getSanitisedUri(_client, '$path'); + final String body = json.encode(bodyData); + _saveChanges(); final Response response = await _client.post(url, headers: { keyHeaderRevocableSession: '1', }, - body: json.encode(bodyData)); + body: body); return _handleResponse( this, response, ParseApiRQ.signUp, _debug, parseClassName); @@ -162,7 +164,7 @@ class ParseUser extends ParseObject implements ParseCloneable { final Uri url = getSanitisedUri(_client, '$keyEndPointLogin', queryParams: queryParams); - + _saveChanges(); final Response response = await _client.get(url, headers: { keyHeaderRevocableSession: '1', @@ -356,7 +358,6 @@ class ParseUser extends ParseObject implements ParseCloneable { final Map responseData = jsonDecode(response.body); if (responseData.containsKey(keyVarObjectId)) { - parseResponse.result.fromJson(responseData); user.sessionToken = responseData[keyParamSessionToken]; ParseCoreData().setSessionId(user.sessionToken); } From b2ae8ec68408c3ec3c0f1530cece8cc28948a3a3 Mon Sep 17 00:00:00 2001 From: Phill Date: Tue, 9 Jul 2019 17:12:33 +0100 Subject: [PATCH 04/25] v1.0.23 - Updated dependencies --- pubspec.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index b6a924a74..bff095a5c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,19 +12,19 @@ dependencies: sdk: flutter # Networking - web_socket_channel: ^1.0.9 - http: ^0.12.0 + web_socket_channel: ^1.0.13 + http: ^0.12.0+2 #Database - sembast: ^1.15.1 + sembast: ^1.16.0+3 xxtea: ^2.0.2 - shared_preferences: ^0.5.2 + shared_preferences: ^0.5.3+2 # Utils - path_provider: ^0.5.0+1 + path_provider: ^1.1.2 uuid: ^2.0.0 - package_info: ^0.4.0 + package_info: ^0.4.0+4 devicelocale: ^0.1.1 dev_dependencies: From 864461052b1a663502f26075ecba73a76860e943 Mon Sep 17 00:00:00 2001 From: Zenz Date: Thu, 11 Jul 2019 10:42:54 -0500 Subject: [PATCH 05/25] same fix for version 1.0.23 (#218) --- lib/src/network/parse_live_query.dart | 6 +++--- lib/src/objects/parse_base.dart | 21 ++++++++++----------- lib/src/objects/parse_installation.dart | 8 ++++---- lib/src/objects/parse_object.dart | 14 ++++++-------- lib/src/objects/parse_user.dart | 10 ++++------ 5 files changed, 27 insertions(+), 32 deletions(-) diff --git a/lib/src/network/parse_live_query.dart b/lib/src/network/parse_live_query.dart index 5c4a48f46..c368efa8c 100644 --- a/lib/src/network/parse_live_query.dart +++ b/lib/src/network/parse_live_query.dart @@ -11,7 +11,8 @@ class LiveQuery { securityContext: ParseCoreData().securityContext); _debug = isDebugEnabled(objectLevelDebug: debug); - _sendSessionId = autoSendSessionId ?? ParseCoreData().autoSendSessionId ?? true; + _sendSessionId = + autoSendSessionId ?? ParseCoreData().autoSendSessionId ?? true; } WebSocket _webSocket; @@ -40,7 +41,6 @@ class LiveQuery { // ignore: always_specify_types Future subscribe(QueryBuilder query) async { - String _liveQueryURL = _client.data.liveQueryURL; if (_liveQueryURL.contains('https')) { _liveQueryURL = _liveQueryURL.replaceAll('https', 'wss'); @@ -101,7 +101,7 @@ class LiveQuery { if (_debug) { print('$_printConstLiveQuery: Done'); } - }, onError: (Error error) { + }, onError: (Object error) { if (_debug) { print( '$_printConstLiveQuery: Error: ${error.runtimeType.toString()}'); diff --git a/lib/src/objects/parse_base.dart b/lib/src/objects/parse_base.dart index 795f06391..ccb9e72a5 100644 --- a/lib/src/objects/parse_base.dart +++ b/lib/src/objects/parse_base.dart @@ -41,12 +41,13 @@ abstract class ParseBase { if (_dirty || _unsavedChanges.isNotEmpty) { return true; } + bool match = false; _getObjectData().forEach((String key, dynamic value) { if (value is ParseObject && value._areChildrenDirty(seenObjects)) { - return true; + match = true; } }); - return false; + return match; } /// Returns [DateTime] createdAt @@ -88,9 +89,8 @@ abstract class ParseBase { map[keyVarUpdatedAt] = _parseDateFormat.format(updatedAt); } - final Map target = forApiRQ - ? _unsavedChanges - : _getObjectData(); + final Map target = + forApiRQ ? _unsavedChanges : _getObjectData(); target.forEach((String key, dynamic value) { if (!map.containsKey(key)) { map[key] = parseEncode(value, full: full); @@ -102,6 +102,7 @@ abstract class ParseBase { map.remove(keyVarUpdatedAt); map.remove(keyVarClassName); //map.remove(keyVarAcl); + map.remove(keyVarObjectId); map.remove(keyParamSessionToken); } @@ -172,11 +173,11 @@ abstract class ParseBase { void operator []=(String key, dynamic value) { set(key, value); } + /// Saves in storage Future saveInStorage(String key) async { final String objectJson = json.encode(toJson(full: true)); - ParseCoreData().getStore() - ..setString(key, objectJson); + ParseCoreData().getStore()..setString(key, objectJson); } /// Sets type [T] from objectData @@ -240,8 +241,7 @@ abstract class ParseBase { await unpin(); final Map objectMap = parseEncode(this, full: true); final String json = jsonEncode(objectMap); - ParseCoreData().getStore() - ..setString(objectId, json); + ParseCoreData().getStore()..setString(objectId, json); return true; } else { return false; @@ -253,8 +253,7 @@ abstract class ParseBase { /// Replicates Android SDK pin process and saves object to storage Future unpin({String key}) async { if (objectId != null) { - ParseCoreData().getStore() - ..remove(key ?? objectId); + ParseCoreData().getStore()..remove(key ?? objectId); return true; } diff --git a/lib/src/objects/parse_installation.dart b/lib/src/objects/parse_installation.dart index dfec33f27..f94b5db61 100644 --- a/lib/src/objects/parse_installation.dart +++ b/lib/src/objects/parse_installation.dart @@ -9,7 +9,7 @@ class ParseInstallation extends ParseObject { _client = client ?? ParseHTTPClient( sendSessionId: - autoSendSessionId ?? ParseCoreData().autoSendSessionId, + autoSendSessionId ?? ParseCoreData().autoSendSessionId, securityContext: ParseCoreData().securityContext); } @@ -127,14 +127,14 @@ class ParseInstallation extends ParseObject { /// Gets the locally stored installation static Future _getFromLocalStore() async { - final CoreStore coreStore = await ParseCoreData().getStore(); + final CoreStore coreStore = ParseCoreData().getStore(); final String installationJson = - await coreStore.getString(keyParseStoreInstallation); + await coreStore.getString(keyParseStoreInstallation); if (installationJson != null) { final Map installationMap = - json.decode(installationJson); + json.decode(installationJson); if (installationMap != null) { return ParseInstallation()..fromJson(installationMap); diff --git a/lib/src/objects/parse_object.dart b/lib/src/objects/parse_object.dart index 2c790c2c8..d60d38a46 100644 --- a/lib/src/objects/parse_object.dart +++ b/lib/src/objects/parse_object.dart @@ -104,8 +104,7 @@ class ParseObject extends ParseBase implements ParseCloneable { if (response != null) { if (response.success) { _savingChanges.clear(); - } - else { + } else { _revertSavingChanges(); } return response; @@ -163,9 +162,9 @@ class ParseObject extends ParseBase implements ParseCloneable { final List requests = chunk.map((ParseObject obj) { return obj._getRequestJson(obj.objectId == null ? 'POST' : 'PUT'); }).toList(); - chunk.forEach((ParseObject obj) { + for (ParseObject obj in chunk) { obj._saveChanges(); - }); + } final ParseResponse response = await batchRequest(requests, chunk); totalResponse.success &= response.success; if (response.success) { @@ -175,16 +174,15 @@ class ParseObject extends ParseBase implements ParseCloneable { if (response.results[i] is ParseError) { // Batch request succeed, but part of batch failed. chunk[i]._revertSavingChanges(); - } - else { + } else { chunk[i]._savingChanges.clear(); } } } else { // If there was an error, we want to roll forward the save changes before rethrowing. - chunk.forEach((ParseObject obj) { + for (ParseObject obj in chunk) { obj._revertSavingChanges(); - }); + } totalResponse.statusCode = response.statusCode; totalResponse.error = response.error; } diff --git a/lib/src/objects/parse_user.dart b/lib/src/objects/parse_user.dart index f2b99e2f5..ead19be56 100644 --- a/lib/src/objects/parse_user.dart +++ b/lib/src/objects/parse_user.dart @@ -33,8 +33,7 @@ class ParseUser extends ParseObject implements ParseCloneable { @override dynamic clone(Map map) => - ParseUser.clone(map) - ..fromJson(map); + ParseUser.clone(map)..fromJson(map); static const String keyEmailVerified = 'emailVerified'; static const String keyUsername = 'username'; @@ -273,9 +272,8 @@ class ParseUser extends ParseObject implements ParseCloneable { final Response response = await _client.post( '${_client.data.serverUrl}$keyEndPointRequestPasswordReset', body: json.encode({keyVarEmail: emailAddress})); - return _handleResponse( - this, response, ParseApiRQ.requestPasswordReset, _debug, - parseClassName); + return _handleResponse(this, response, ParseApiRQ.requestPasswordReset, + _debug, parseClassName); } on Exception catch (e) { return handleException( e, ParseApiRQ.requestPasswordReset, _debug, parseClassName); @@ -334,7 +332,7 @@ class ParseUser extends ParseObject implements ParseCloneable { static Future _getUserFromLocalStore( {ParseCloneable cloneable}) async { - final CoreStore coreStore = await ParseCoreData().getStore(); + final CoreStore coreStore = ParseCoreData().getStore(); final String userJson = await coreStore.getString(keyParseStoreUser); if (userJson != null) { From b8d8da387a03eb00991ea5d6cd0e9dbb0147cf7a Mon Sep 17 00:00:00 2001 From: Phill Date: Thu, 11 Jul 2019 16:47:19 +0100 Subject: [PATCH 06/25] v1.0.23 - Updated dependencies --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index bff095a5c..b209c4816 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ dependencies: http: ^0.12.0+2 #Database - sembast: ^1.16.0+3 + sembast: ^1.17.0 xxtea: ^2.0.2 shared_preferences: ^0.5.3+2 From 0131c333eb4bedaec2ea4fe5a54d839f8bd49d69 Mon Sep 17 00:00:00 2001 From: Phill Date: Thu, 11 Jul 2019 20:46:59 +0100 Subject: [PATCH 07/25] v1.0.23 - Added unset --- lib/src/enums/parse_enum_api_rq.dart | 1 + lib/src/objects/parse_base.dart | 2 -- lib/src/objects/parse_object.dart | 36 ++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/lib/src/enums/parse_enum_api_rq.dart b/lib/src/enums/parse_enum_api_rq.dart index ae4651151..56bb33ef8 100644 --- a/lib/src/enums/parse_enum_api_rq.dart +++ b/lib/src/enums/parse_enum_api_rq.dart @@ -5,6 +5,7 @@ enum ParseApiRQ { healthCheck, get, getAll, + unset, create, save, query, diff --git a/lib/src/objects/parse_base.dart b/lib/src/objects/parse_base.dart index ccb9e72a5..c9c548f61 100644 --- a/lib/src/objects/parse_base.dart +++ b/lib/src/objects/parse_base.dart @@ -186,7 +186,6 @@ abstract class ParseBase { /// [bool] forceUpdate is always true, if unsure as to whether an item is /// needed or not, set to false void set(String key, T value, {bool forceUpdate = true}) { - if (value != null) { if (_getObjectData().containsKey(key)) { if (_getObjectData()[key] == value) { return; @@ -198,7 +197,6 @@ abstract class ParseBase { _getObjectData()[key] = value; } _unsavedChanges[key] = value; - } } ///Set the [ParseACL] governing this object. diff --git a/lib/src/objects/parse_object.dart b/lib/src/objects/parse_object.dart index d60d38a46..0eea7cead 100644 --- a/lib/src/objects/parse_object.dart +++ b/lib/src/objects/parse_object.dart @@ -469,6 +469,42 @@ class ParseObject extends ParseBase implements ParseCloneable { } } + /// Can be used set an objects variable to undefined rather than null + Future unset(String key) async { + final dynamic object = _objectData[key]; + _objectData.remove(key); + _unsavedChanges.remove(key); + _savingChanges.remove(key); + + try { + if (objectId != null) { + final Uri url = getSanitisedUri(_client, '$_path/$objectId'); + final String body = '{\"$key\":{\"__op\":\"Delete\"}}'; + final Response result = await _client.put(url, body: body); + final ParseResponse response = handleResponse( + this, result, ParseApiRQ.unset, _debug, parseClassName); + if (!response.success) { + _objectData[key] = object; + _unsavedChanges[key] = object; + _savingChanges[key] = object; + } else { + return ParseResponse() + ..success = true; + } + } else { + return ParseResponse() + ..success = true; + } + } on Exception catch (e) { + _objectData[key] = object; + _unsavedChanges[key] = object; + _savingChanges[key] = object; + } + + return ParseResponse() + ..success = false; + } + /// Can be used to create custom queries Future query(String query) async { try { From 4cd0a3ee3f53fb3d5bbcb249731081ab424cae0d Mon Sep 17 00:00:00 2001 From: Phill Date: Thu, 11 Jul 2019 20:56:47 +0100 Subject: [PATCH 08/25] v1.0.23 - Fixed unset --- lib/src/objects/parse_base.dart | 2 ++ lib/src/objects/parse_object.dart | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/src/objects/parse_base.dart b/lib/src/objects/parse_base.dart index c9c548f61..ccb9e72a5 100644 --- a/lib/src/objects/parse_base.dart +++ b/lib/src/objects/parse_base.dart @@ -186,6 +186,7 @@ abstract class ParseBase { /// [bool] forceUpdate is always true, if unsure as to whether an item is /// needed or not, set to false void set(String key, T value, {bool forceUpdate = true}) { + if (value != null) { if (_getObjectData().containsKey(key)) { if (_getObjectData()[key] == value) { return; @@ -197,6 +198,7 @@ abstract class ParseBase { _getObjectData()[key] = value; } _unsavedChanges[key] = value; + } } ///Set the [ParseACL] governing this object. diff --git a/lib/src/objects/parse_object.dart b/lib/src/objects/parse_object.dart index 0eea7cead..c28b96cac 100644 --- a/lib/src/objects/parse_object.dart +++ b/lib/src/objects/parse_object.dart @@ -470,12 +470,19 @@ class ParseObject extends ParseBase implements ParseCloneable { } /// Can be used set an objects variable to undefined rather than null - Future unset(String key) async { + /// + /// If object is not saved remotely, set offlineOnly to true to avoid api calls. + Future unset(String key, {bool offlineOnly = false}) async { final dynamic object = _objectData[key]; _objectData.remove(key); _unsavedChanges.remove(key); _savingChanges.remove(key); + if (offlineOnly) { + return ParseResponse() + ..success = true; + } + try { if (objectId != null) { final Uri url = getSanitisedUri(_client, '$_path/$objectId'); @@ -491,9 +498,6 @@ class ParseObject extends ParseBase implements ParseCloneable { return ParseResponse() ..success = true; } - } else { - return ParseResponse() - ..success = true; } } on Exception catch (e) { _objectData[key] = object; From 7d0e0804d6506937da9b3986c6f744295ba1167c Mon Sep 17 00:00:00 2001 From: Phill Date: Sat, 13 Jul 2019 08:51:34 +0100 Subject: [PATCH 09/25] v1.0.23 - Added Facebook login helper --- lib/src/objects/parse_object.dart | 2 +- lib/src/utils/parse_login_helpers.dart | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 lib/src/utils/parse_login_helpers.dart diff --git a/lib/src/objects/parse_object.dart b/lib/src/objects/parse_object.dart index c28b96cac..69140ad51 100644 --- a/lib/src/objects/parse_object.dart +++ b/lib/src/objects/parse_object.dart @@ -499,7 +499,7 @@ class ParseObject extends ParseBase implements ParseCloneable { ..success = true; } } - } on Exception catch (e) { + } on Exception { _objectData[key] = object; _unsavedChanges[key] = object; _savingChanges[key] = object; diff --git a/lib/src/utils/parse_login_helpers.dart b/lib/src/utils/parse_login_helpers.dart new file mode 100644 index 000000000..24ae3b586 --- /dev/null +++ b/lib/src/utils/parse_login_helpers.dart @@ -0,0 +1,7 @@ +Map facebookLogin(String token, String id, DateTime expires) { + return { + 'access_token': token, + 'id': id, + 'expiration_date': expires.toString() + }; +} From 0a4e8dca1ae964398e9448f8c3725edfcc4b61ba Mon Sep 17 00:00:00 2001 From: Phill Date: Sat, 13 Jul 2019 08:54:48 +0100 Subject: [PATCH 10/25] v1.0.23 - Added Facebook login helper --- lib/parse_server_sdk.dart | 2 ++ lib/src/utils/parse_login_helpers.dart | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/parse_server_sdk.dart b/lib/parse_server_sdk.dart index 7bf8a6285..0a276e93e 100644 --- a/lib/parse_server_sdk.dart +++ b/lib/parse_server_sdk.dart @@ -90,6 +90,8 @@ part 'src/utils/parse_logger.dart'; part 'src/utils/parse_utils.dart'; +part 'src/utils/parse_login_helpers.dart'; + class Parse { ParseCoreData data; bool _hasBeenInitialized = false; diff --git a/lib/src/utils/parse_login_helpers.dart b/lib/src/utils/parse_login_helpers.dart index 24ae3b586..ae7459de7 100644 --- a/lib/src/utils/parse_login_helpers.dart +++ b/lib/src/utils/parse_login_helpers.dart @@ -1,4 +1,6 @@ -Map facebookLogin(String token, String id, DateTime expires) { +part of flutter_parse_sdk; + +Map facebook(String token, String id, DateTime expires) { return { 'access_token': token, 'id': id, From 3b3ab3422f32cf617c5236e78574d3711af691b0 Mon Sep 17 00:00:00 2001 From: Phill Date: Sat, 13 Jul 2019 09:09:01 +0100 Subject: [PATCH 11/25] v1.0.23 - Added Facebook README details --- README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/README.md b/README.md index 2fd12bdc6..5e8c5c37a 100644 --- a/README.md +++ b/README.md @@ -457,6 +457,38 @@ Other user features are:- * Save * Destroy user * Queries + + ## Facebook, OAuth and 3rd Party Login/User + + Usually, each provider will provide their own library for logins, but the loginWith method on ParseUser accepts a name of provider, then a Map with the authentication details required. + For Facebook and the example below, we used the library provided at https://pub.dev/packages/flutter_facebook_login + + ``` + Future goToFacebookLogin() async { + final FacebookLogin facebookLogin = FacebookLogin(); + final FacebookLoginResult result = await facebookLogin.logInWithReadPermissions(['email']); + + switch (result.status) { + case FacebookLoginStatus.loggedIn: + final ParseResponse response = await ParseUser.loginWith( + 'facebook', + facebook(result.accessToken.token, + result.accessToken.userId, + result.accessToken.expires)); + + if (response.success) { + // User is logged in, test with ParseUser.currentUser() + } + break; + case FacebookLoginStatus.cancelledByUser: + // User cancelled + break; + case FacebookLoginStatus.error: + // Error + break; + } + } +``` ## Security for Objects - ParseACL For any object, you can specify which users are allowed to read the object, and which users are allowed to modify an object. From 789bdafbc0a9c35b012366fc128fedc219cbdfbb Mon Sep 17 00:00:00 2001 From: Phill Date: Sat, 13 Jul 2019 14:01:03 +0100 Subject: [PATCH 12/25] v1.0.23 - Added authData object --- lib/src/objects/parse_user.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/src/objects/parse_user.dart b/lib/src/objects/parse_user.dart index ead19be56..fd7e95702 100644 --- a/lib/src/objects/parse_user.dart +++ b/lib/src/objects/parse_user.dart @@ -68,6 +68,11 @@ class ParseUser extends ParseObject implements ParseCloneable { set sessionToken(String sessionToken) => set(keyVarSessionToken, sessionToken); + String get authData => super.get(keyVarAuthData); + + set authData(String authData) => + set(keyVarAuthData, authData); + static ParseUser createUser( [String username, String password, String emailAddress]) { return ParseUser(username, password, emailAddress); From 2ed183c19532affc0f4f792f8bcb4dcee0eb67b5 Mon Sep 17 00:00:00 2001 From: Phill Date: Sat, 13 Jul 2019 14:09:26 +0100 Subject: [PATCH 13/25] v1.0.23 - Added authData object --- lib/src/base/parse_constants.dart | 1 + lib/src/network/parse_live_query.dart | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/src/base/parse_constants.dart b/lib/src/base/parse_constants.dart index 0fbac1854..b57ff5183 100644 --- a/lib/src/base/parse_constants.dart +++ b/lib/src/base/parse_constants.dart @@ -25,6 +25,7 @@ const String keyVarUsername = 'username'; const String keyVarEmail = 'email'; const String keyVarPassword = 'password'; const String keyVarSessionToken = 'sessionToken'; +const String keyVarAuthData = 'authData'; const String keyVarAcl = 'ACL'; const String keyVarName = 'name'; const String keyVarURL = 'url'; diff --git a/lib/src/network/parse_live_query.dart b/lib/src/network/parse_live_query.dart index c368efa8c..7d944a106 100644 --- a/lib/src/network/parse_live_query.dart +++ b/lib/src/network/parse_live_query.dart @@ -114,13 +114,17 @@ class LiveQuery { //It should be the first message sent from a client after the WebSocket connection is established. _connectMessage = { 'op': 'connect', - 'applicationId': _client.data.applicationId, - 'clientKey': _client.data.clientKey ?? '' + 'applicationId': _client.data.applicationId }; if (_sendSessionId) { _connectMessage['sessionToken'] = _client.data.sessionId; } + if (_client.data.clientKey != null) + _connectMessage['clientKey'] = _client.data.clientKey; + if (_client.data.masterKey != null) + _connectMessage['masterKey'] = _client.data.masterKey; + if (_debug) { print('$_printConstLiveQuery: ConnectMessage: $_connectMessage'); } From 3f871b0a23eff0101ec0af90f2068503374dc8f9 Mon Sep 17 00:00:00 2001 From: Phill Date: Sat, 13 Jul 2019 15:07:59 +0100 Subject: [PATCH 14/25] v1.0.23 - Corrected AuthData type --- lib/src/objects/parse_user.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/src/objects/parse_user.dart b/lib/src/objects/parse_user.dart index fd7e95702..1cfa0cccb 100644 --- a/lib/src/objects/parse_user.dart +++ b/lib/src/objects/parse_user.dart @@ -68,10 +68,11 @@ class ParseUser extends ParseObject implements ParseCloneable { set sessionToken(String sessionToken) => set(keyVarSessionToken, sessionToken); - String get authData => super.get(keyVarAuthData); + Map get authData => + super.get>(keyVarAuthData); - set authData(String authData) => - set(keyVarAuthData, authData); + set authData(Map authData) => + set>(keyVarAuthData, authData); static ParseUser createUser( [String username, String password, String emailAddress]) { From 33c4de120c031896d31c8096e47ab63017918879 Mon Sep 17 00:00:00 2001 From: Dima Rostopira Date: Sat, 13 Jul 2019 21:34:37 +0300 Subject: [PATCH 15/25] Support keysToReturn limiter in livequery (#222) --- lib/src/network/parse_live_query.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/src/network/parse_live_query.dart b/lib/src/network/parse_live_query.dart index 7d944a106..bbc196683 100644 --- a/lib/src/network/parse_live_query.dart +++ b/lib/src/network/parse_live_query.dart @@ -49,6 +49,7 @@ class LiveQuery { } final String _className = query.object.parseClassName; + final keysToReturn = query.limiters['keys']?.split(','); query.limiters.clear(); //Remove limits in LiveQuery final String _where = query._buildQuery().replaceAll('where=', ''); @@ -138,6 +139,8 @@ class LiveQuery { 'query': { 'className': _className, 'where': _whereMap, + if (keysToReturn != null && keysToReturn.length > 0) + 'fields': keysToReturn } }; if (_sendSessionId) { From efd0a0fc22a617ef69898ece6cfeb8709cecaced Mon Sep 17 00:00:00 2001 From: Dima Rostopira Date: Fri, 19 Jul 2019 13:04:57 +0300 Subject: [PATCH 16/25] Fix crash on Flutter Web (#223) --- lib/src/network/parse_http_client.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/network/parse_http_client.dart b/lib/src/network/parse_http_client.dart index 07c07e5c4..85f995450 100644 --- a/lib/src/network/parse_http_client.dart +++ b/lib/src/network/parse_http_client.dart @@ -6,7 +6,7 @@ class ParseHTTPClient extends BaseClient { : _sendSessionId = sendSessionId, _client = securityContext != null ? IOClient(HttpClient(context: securityContext)) - : IOClient(); + : Client(); final Client _client; final bool _sendSessionId; From beb98fecd869f27419a2ec1deaa5b09ea5d3479a Mon Sep 17 00:00:00 2001 From: kidder <841497533@qq.com> Date: Fri, 19 Jul 2019 18:36:12 +0800 Subject: [PATCH 17/25] add distinct function (#225) * Merged v1.0.22 * 1. Fix return result from save method. This is important. (#208) 2. Fix unsavedChanges issue in ParseUser login and signUp method. 3. Delete useless and reduplicative decode in ParseUser. Because this is already done in handleResponse method. This also cause wrong unsaved changes. * README.md updated from https://stackedit.io/ * README.md updated from https://stackedit.io/ * README.md updated from https://stackedit.io/ * README.md updated from https://stackedit.io/ * README.md updated from https://stackedit.io/ * Update README.md * add distinct --- .gitignore | 2 +- README.md | 87 +++++++------------ lib/src/base/parse_constants.dart | 1 + lib/src/network/parse_query.dart | 11 ++- lib/src/objects/parse_object.dart | 13 +++ .../response/parse_response_builder.dart | 14 ++- 6 files changed, 64 insertions(+), 64 deletions(-) diff --git a/.gitignore b/.gitignore index c4f26e041..5cb586d1b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .DS_Store .dart_tool/ - +.history .packages .pub/ pubspec.lock diff --git a/README.md b/README.md index 5e8c5c37a..6930b1a2f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ -![enter image description here](https://upload.wikimedia.org/wikipedia/commons/1/17/Google-flutter-logo.png) -![enter image description here](https://i2.wp.com/blog.openshift.com/wp-content/uploads/parse-server-logo-1.png?fit=200%2C200&ssl=1&resize=350%2C200) -[![Build Status](https://travis-ci.org/phillwiggins/flutter_parse_sdk.svg?branch=master)](https://travis-ci.org/phillwiggins/flutter_parse_sdk) +![Parse Logo](https://upload.wikimedia.org/wikipedia/commons/1/17/Google-flutter-logo.png)![Flutter Logo](https://i2.wp.com/blog.openshift.com/wp-content/uploads/parse-server-logo-1.png?fit=200%2C200&ssl=1&resize=350%2C200) ## Parse For Flutter! Hi, this is a Flutter plugin that allows communication with a Parse Server, (https://parseplatform.org) either hosted on your own server or another, like (http://Back4App.com). @@ -23,31 +21,32 @@ or clone this repository and add to your project. As this is an early developmen Once you have the library added to your project, upon first call to your app (Similar to what your application class would be) add the following... ```dart - await Parse().initialize( - ApplicationConstants.keyApplicationId, - ApplicationConstants.keyParseServerUrl); +await Parse().initialize( + keyApplicationId, + keyParseServerUrl); ``` -if you want to use secure storage also that's allow using sdk on desktop application + +If you want to use secure storage or use the Flutter web/desktop SDK, please change to the below instance of CoreStorage as it has no dependencies on Flutter. ```dart - await Parse().initialize(keyParseApplicationId, keyParseServerUrl, - masterKey: keyParseMasterKey, - debug: true, - coreStore: await CoreStoreSembastImp.getInstance()); +await Parse().initialize( + keyParseApplicationId, + keyParseServerUrl, + coreStore: await CoreStoreSembastImp.getInstance()); ``` -It's possible to add other params, such as ... +It's possible to add other parameters to work with your instance of Parse Server:- ```dart await Parse().initialize( - ApplicationConstants.keyApplicationId, - ApplicationConstants.keyParseServerUrl, - masterKey: ApplicationConstants.keyParseMasterKey, - clientKey: ApplicationConstants.keyParseClientKey, - debug: true, - liveQueryUrl: ApplicationConstants.keyLiveQueryUrl, - autoSendSessionId: true, - securityContext: securityContext, - coreStore: await CoreStoreSharedPrefsImp.getInstance()); + keyApplicationId, + keyParseServerUrl, + masterKey: keyParseMasterKey, // Required for Back4App and others + clientKey: keyParseClientKey, // Required for some setups + debug: true, // When enabled, prints logs to console + liveQueryUrl: keyLiveQueryUrl, // Required if using LiveQuery + autoSendSessionId: true, // Some confurations require this to be true + securityContext: securityContext, // Again, required for some setups + coreStore: await CoreStoreSharedPrefsImp.getInstance()); // Will use SharedPreferences instead of Sembast as an internal DB ``` ## Objects @@ -56,13 +55,13 @@ You can create custom objects by calling: var dietPlan = ParseObject('DietPlan') ..set('Name', 'Ketogenic') ..set('Fat', 65); -await dietPlan.save() +await dietPlan.save(); ``` Verify that the object has been successfully saved using ```dart var response = await dietPlan.save(); if (response.success) { - dietPlan = response.result; + dietPlan = response.results.first; } ``` Types supported: @@ -134,14 +133,6 @@ and to retrieve it var dietPlan = DietPlan().fromPin('OBJECT ID OF OBJECT'); ``` -## Storage -We now have 2 types of storage, secure and unsecure. We currently rely on 2 third party options: - - * SharedPreferences - * Sembast - -Sembast offers secured storage, whilst SharePreferences wraps NSUserDefaults (on iOS) and SharedPreferences (on Android). - ## Increment Counter values in objects Retrieve it, call @@ -172,9 +163,9 @@ var response = await dietPlan.remove("listKeywords", ["a"]); or using with save function ```dart -dietPlan.setAddAll('listKeywords', ['a','a','d']); -dietPlan.setAddAllUnique('listKeywords', ['a','a','d']); -dietPlan.setRemoveAll('listKeywords', ['a']); +dietPlan.setAdd('listKeywords', ['a','a','d']); +dietPlan.setAddUnique('listKeywords', ['a','a','d']); +dietPlan.setRemove('listKeywords', ['a']); var response = dietPlan.save() ``` @@ -214,7 +205,7 @@ var queryBuilder = QueryBuilder(DietPlan()) var response = await queryBuilder.query(); if (response.success) { - print(ApplicationConstants.keyAppName + ": " + ((response.result as List).first as DietPlan).toString()); + print(ApplicationConstants.keyAppName + ": " + ((response.results as List).first as DietPlan).toString()); } else { print(ApplicationConstants.keyAppName + ": " + response.exception.message); } @@ -304,11 +295,11 @@ The Parse Server configuration guide on the server is found here https://docs.pa Initialize the Parse Live Query by entering the parameter liveQueryUrl in Parse().initialize: ```dart Parse().initialize( - ApplicationConstants.keyApplicationId, - ApplicationConstants.keyParseServerUrl, - clientKey: ApplicationConstants.keyParseClientKey, + keyApplicationId, + keyParseServerUrl, + clientKey: keyParseClientKey, debug: true, - liveQueryUrl: ApplicationConstants.keyLiveQueryUrl, + liveQueryUrl: keyLiveQueryUrl, autoSendSessionId: true); ``` @@ -580,21 +571,6 @@ final Map params = {'plan': 'paid'}; function.execute(parameters: params); ``` -## Relation -The SDK supports Relation. - -To Retrive a relation instance for user, call: -```dart -final relation = user.getRelation('dietPlans'); -``` - -and then you can add a relation to the passed in object. - -```dart -relation.add(dietPlan); -final result = await user.save(); -``` - ## Other Features of this library Main: * Installation (View the example application) @@ -616,6 +592,3 @@ Objects: ## Author:- This project was authored by Phill Wiggins. You can contact me at phill.wiggins@gmail.com - diff --git a/lib/src/base/parse_constants.dart b/lib/src/base/parse_constants.dart index b57ff5183..a1b86386f 100644 --- a/lib/src/base/parse_constants.dart +++ b/lib/src/base/parse_constants.dart @@ -15,6 +15,7 @@ const String keyEndPointVerificationEmail = '/verificationEmailRequest'; const String keyEndPointRequestPasswordReset = '/requestPasswordReset'; const String keyEndPointClasses = '/classes/'; const String keyEndPointHealth = '/health'; +const String keyEndPointAggregate = '/aggregate/'; // ParseObject variables const String keyVarClassName = 'className'; diff --git a/lib/src/network/parse_query.dart b/lib/src/network/parse_query.dart index 797fc4182..4c8b06266 100644 --- a/lib/src/network/parse_query.dart +++ b/lib/src/network/parse_query.dart @@ -246,7 +246,8 @@ class QueryBuilder { // Add a constraint to the query that requires a particular key's value match another QueryBuilder // ignore: always_specify_types void whereMatchesQuery(String column, QueryBuilder query) { - final String inQuery = query._buildQueryRelational(query.object.parseClassName); + final String inQuery = + query._buildQueryRelational(query.object.parseClassName); queries.add(MapEntry( _SINGLE_QUERY, '\"$column\":{\"\$inQuery\":$inQuery}')); @@ -255,7 +256,8 @@ class QueryBuilder { //Add a constraint to the query that requires a particular key's value does not match another QueryBuilder // ignore: always_specify_types void whereDoesNotMatchQuery(String column, QueryBuilder query) { - final String inQuery = query._buildQueryRelational(query.object.parseClassName); + final String inQuery = + query._buildQueryRelational(query.object.parseClassName); queries.add(MapEntry( _SINGLE_QUERY, '\"$column\":{\"\$notInQuery\":$inQuery}')); @@ -268,6 +270,11 @@ class QueryBuilder { return object.query(_buildQuery()); } + Future distinct(String className) async { + String queryString = "distinct=$className"; + return object.distinct(queryString); + } + ///Counts the number of objects that match this query Future count() async { return object.query(_buildQueryCount()); diff --git a/lib/src/objects/parse_object.dart b/lib/src/objects/parse_object.dart index 69140ad51..cc3ec81fe 100644 --- a/lib/src/objects/parse_object.dart +++ b/lib/src/objects/parse_object.dart @@ -12,6 +12,7 @@ class ParseObject extends ParseBase implements ParseCloneable { : super() { parseClassName = className; _path = '$keyEndPointClasses$className'; + _aggregatepath = '$keyEndPointAggregate$className'; _debug = isDebugEnabled(objectLevelDebug: debug); _client = client ?? @@ -28,6 +29,7 @@ class ParseObject extends ParseBase implements ParseCloneable { ParseObject.clone(parseClassName)..fromJson(map); String _path; + String _aggregatepath; bool _debug; ParseHTTPClient _client; @@ -521,6 +523,17 @@ class ParseObject extends ParseBase implements ParseCloneable { } } + Future distinct(String query) async { + try { + final Uri url = getSanitisedUri(_client, '$_aggregatepath', query: query); + final Response result = await _client.get(url); + return handleResponse( + this, result, ParseApiRQ.query, _debug, parseClassName); + } on Exception catch (e) { + return handleException(e, ParseApiRQ.query, _debug, parseClassName); + } + } + /// Deletes the current object locally and online Future delete({String id, String path}) async { try { diff --git a/lib/src/objects/response/parse_response_builder.dart b/lib/src/objects/response/parse_response_builder.dart index 782de2673..893010503 100644 --- a/lib/src/objects/response/parse_response_builder.dart +++ b/lib/src/objects/response/parse_response_builder.dart @@ -91,10 +91,16 @@ class _ParseResponseBuilder { response.result = map; } else if (map != null && map.length == 1 && map.containsKey('results')) { final List results = map['results']; - final List items = _handleMultipleResults(object, results); - response.results = items; - response.result = items; - response.count = items.length; + if (results[0] is String) { + response.results = results; + response.result = results; + response.count = results.length; + } else { + final List items = _handleMultipleResults(object, results); + response.results = items; + response.result = items; + response.count = items.length; + } } else if (map != null && map.length == 2 && map.containsKey('count')) { final List results = [map['count']]; response.results = results; From 0cffb99adeff5c693acfde8dfc54e5f9d6260514 Mon Sep 17 00:00:00 2001 From: Douglas Muraoka Date: Thu, 25 Jul 2019 17:04:41 -0300 Subject: [PATCH 18/25] fix(LiveQuery): Session token null when subscribing (#228) When `autoSendSessionId: true`, it always sets the sessionToken header, even if `null`. However, when `null`, it throws an exception due to its unexpected type and breaks live query. ``` I/flutter (12537): LiveQuery: : ConnectMessage: {op: connect, applicationId: myAppId, masterKey: 123456, sessionToken: null} I/flutter (12537): LiveQuery: : SubscribeMessage: {op: subscribe, requestId: 1, query: {className: Diet_Plans, where: {objectId: 2pNUgv1CKA}}, sessionToken: null} I/flutter (12537): LiveQuery: : Listen: {"op":"error","error":"Invalid type: null (expected string)","code":1,"reconnect":true} ``` --- lib/src/network/parse_live_query.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/network/parse_live_query.dart b/lib/src/network/parse_live_query.dart index bbc196683..b870d1fdc 100644 --- a/lib/src/network/parse_live_query.dart +++ b/lib/src/network/parse_live_query.dart @@ -117,7 +117,7 @@ class LiveQuery { 'op': 'connect', 'applicationId': _client.data.applicationId }; - if (_sendSessionId) { + if (_sendSessionId && _client.data.sessionId != null) { _connectMessage['sessionToken'] = _client.data.sessionId; } @@ -143,7 +143,7 @@ class LiveQuery { 'fields': keysToReturn } }; - if (_sendSessionId) { + if (_sendSessionId && _client.data.sessionId != null) { _subscribeMessage['sessionToken'] = _client.data.sessionId; } From d857c3a58277e394db359001c3bdbc8b4bad3013 Mon Sep 17 00:00:00 2001 From: VigneshPasupathy Date: Tue, 30 Jul 2019 23:45:15 +0530 Subject: [PATCH 19/25] Allow adding additional columns to ParseUser through the set call (#232) --- README.md | 7 +++++++ lib/src/objects/parse_user.dart | 5 +---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6930b1a2f..7b94200b3 100644 --- a/README.md +++ b/README.md @@ -441,6 +441,13 @@ Also, once logged in you can manage sessions tokens. This feature can be called ```dart user = ParseUser.currentUser(); ``` + +To add additional columns to the user: +```dart +var user = ParseUser("TestFlutter", "TestPassword123", "TestFlutterSDK@gmail.com") + ..set("userLocation", "FlutterLand"); +``` + Other user features are:- * Request Password Reset * Verification Email Request diff --git a/lib/src/objects/parse_user.dart b/lib/src/objects/parse_user.dart index 1cfa0cccb..b541eedeb 100644 --- a/lib/src/objects/parse_user.dart +++ b/lib/src/objects/parse_user.dart @@ -136,10 +136,7 @@ class ParseUser extends ParseObject implements ParseCloneable { return null; } - final Map bodyData = {}; - bodyData[keyVarEmail] = emailAddress; - bodyData[keyVarPassword] = password; - bodyData[keyVarUsername] = username; + final Map bodyData = _getObjectData(); final Uri url = getSanitisedUri(_client, '$path'); final String body = json.encode(bodyData); _saveChanges(); From 79d6a4aae267bf1447b08d3da3be638a48b92621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E8=90=A7=E7=8E=89?= Date: Thu, 1 Aug 2019 16:05:14 +0800 Subject: [PATCH 20/25] ParseFile should NOT extends ParseObject. (#233) --- lib/src/objects/parse_base.dart | 2 +- lib/src/objects/parse_file.dart | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/src/objects/parse_base.dart b/lib/src/objects/parse_base.dart index ccb9e72a5..78a4a7205 100644 --- a/lib/src/objects/parse_base.dart +++ b/lib/src/objects/parse_base.dart @@ -23,7 +23,7 @@ abstract class ParseBase { } bool _isDirty(bool considerChildren) { - if (_dirty || _unsavedChanges.isNotEmpty) { + if (_dirty || _unsavedChanges.isNotEmpty || objectId == null) { return true; } diff --git a/lib/src/objects/parse_file.dart b/lib/src/objects/parse_file.dart index 7bef32891..9fa117fd8 100644 --- a/lib/src/objects/parse_file.dart +++ b/lib/src/objects/parse_file.dart @@ -1,6 +1,6 @@ part of flutter_parse_sdk; -class ParseFile extends ParseObject { +class ParseFile extends ParseBase { /// Creates a new file /// /// {https://docs.parseplatform.org/rest/guide/#files/} @@ -9,8 +9,7 @@ class ParseFile extends ParseObject { String url, bool debug, ParseHTTPClient client, - bool autoSendSessionId}) - : super(keyFile) { + bool autoSendSessionId}) { _debug = isDebugEnabled(objectLevelDebug: debug); _client = client ?? ParseHTTPClient( @@ -27,6 +26,9 @@ class ParseFile extends ParseObject { } } + bool _debug; + ParseHTTPClient _client; + File file; String get name => super.get(keyVarName); From 931fdd8640a93727e1fbac0b5789a3762c794471 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E8=90=A7=E7=8E=89?= Date: Thu, 1 Aug 2019 16:32:15 +0800 Subject: [PATCH 21/25] Fix a stupid mistake. (#234) --- lib/src/objects/parse_base.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/objects/parse_base.dart b/lib/src/objects/parse_base.dart index 78a4a7205..33d1073b3 100644 --- a/lib/src/objects/parse_base.dart +++ b/lib/src/objects/parse_base.dart @@ -167,7 +167,7 @@ abstract class ParseBase { } dynamic operator [](Object key) { - get(key); + return get(key); } void operator []=(String key, dynamic value) { From 026efe9d0c103976b25b5cb175b982bf57b18c13 Mon Sep 17 00:00:00 2001 From: manhhavu Date: Fri, 2 Aug 2019 09:54:00 +0200 Subject: [PATCH 22/25] Fix ACL save (#226) (#236) Great work! --- lib/src/objects/parse_base.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/objects/parse_base.dart b/lib/src/objects/parse_base.dart index 33d1073b3..8331ee2db 100644 --- a/lib/src/objects/parse_base.dart +++ b/lib/src/objects/parse_base.dart @@ -203,7 +203,7 @@ abstract class ParseBase { ///Set the [ParseACL] governing this object. void setACL(ParseACL acl) { - _getObjectData()[keyVarAcl] = acl; + set(keyVarAcl, acl); } ///Access the [ParseACL] governing this object. From d351cafcb667679b85a58afe3488312aa9f22e12 Mon Sep 17 00:00:00 2001 From: moorelwang Date: Wed, 7 Aug 2019 15:17:04 +0800 Subject: [PATCH 23/25] The method of setAdd, setRemove, setAddAll, setRemoveAll, Increment, Decrement, AddUnique, AddRelation, RemoveRelation can be merged between the same operation and some other operation. Implementation method reference iOS SDK. I do this by Implementing a tool class in "parse_merge.dart" and calling the "mergeWithPrevious" Method in the set method which implemented in parse_base.dart. --- lib/parse_server_sdk.dart | 8 +- lib/src/objects/parse_base.dart | 13 +- lib/src/objects/parse_merge.dart | 251 +++++++++++++++++++++++++++++++ 3 files changed, 265 insertions(+), 7 deletions(-) create mode 100644 lib/src/objects/parse_merge.dart diff --git a/lib/parse_server_sdk.dart b/lib/parse_server_sdk.dart index 0a276e93e..671c7af85 100644 --- a/lib/parse_server_sdk.dart +++ b/lib/parse_server_sdk.dart @@ -92,6 +92,8 @@ part 'src/utils/parse_utils.dart'; part 'src/utils/parse_login_helpers.dart'; +part 'src/objects/parse_merge.dart'; + class Parse { ParseCoreData data; bool _hasBeenInitialized = false; @@ -116,8 +118,8 @@ class Parse { String masterKey, String sessionId, bool autoSendSessionId, - SecurityContext securityContext, - CoreStore coreStore}) async { + SecurityContext securityContext, + CoreStore coreStore}) async { final String url = removeTrailingSlash(serverUrl); await ParseCoreData.init(appId, url, @@ -147,7 +149,7 @@ class Parse { final ParseHTTPClient _client = client ?? ParseHTTPClient( sendSessionId: - sendSessionIdByDefault ?? ParseCoreData().autoSendSessionId, + sendSessionIdByDefault ?? ParseCoreData().autoSendSessionId, securityContext: ParseCoreData().securityContext); const String className = 'parseBase'; diff --git a/lib/src/objects/parse_base.dart b/lib/src/objects/parse_base.dart index 8331ee2db..5218fb508 100644 --- a/lib/src/objects/parse_base.dart +++ b/lib/src/objects/parse_base.dart @@ -191,13 +191,18 @@ abstract class ParseBase { if (_getObjectData()[key] == value) { return; } - if (forceUpdate) { - _getObjectData()[key] = value; - } + /*if (!forceUpdate) { + if (value is Map) { + value = mergeWithPrevious(_getObjectData()[key], value); + } + }*/ + // value = mergeWithPrevious(_unsavedChanges[key], value); + _getObjectData()[key] = + ParseMergeTool().mergeWithPrevious(_unsavedChanges[key], value); } else { _getObjectData()[key] = value; } - _unsavedChanges[key] = value; + _unsavedChanges[key] = _getObjectData()[key]; } } diff --git a/lib/src/objects/parse_merge.dart b/lib/src/objects/parse_merge.dart new file mode 100644 index 000000000..f800655de --- /dev/null +++ b/lib/src/objects/parse_merge.dart @@ -0,0 +1,251 @@ +part of flutter_parse_sdk; + +class ParseMergeTool { + // merge method + dynamic mergeWithPrevious(dynamic previous, dynamic values) { + if (previous == null) { + return values; + } + String previousAction = 'Set'; + if (previous is Map) { + previousAction = previous['__op']; + } + if (values is Map) { + if (values['__op'] == 'Add') { + values = _mergeWithPreviousAdd(previousAction, previous, values); + } else if (values['__op'] == 'Remove') { + values = _mergeWithPreviousRemove(previousAction, previous, values); + } else if (values['__op'] == 'Increment') { + values = _mergeWithPreviousIncrement(previousAction, previous, values); + } else if (values['__op'] == 'AddUnique') { + values = _mergeWithPreviousAddUnique(previousAction, previous, values); + } else if (values['__op'] == 'AddRelation') { + values = + _mergeWithPreviousAddRelation(previousAction, previous, values); + } else if (values['__op'] == 'RemoveRelation') { + values = + _mergeWithPreviousRemoveRelation(previousAction, previous, values); + } + } + return values; + } + + // Add operation Merge + dynamic _mergeWithPreviousAdd( + String previousAction, dynamic previous, dynamic values) { + if (previousAction == 'Set') { + if (previous is List) { + return List.from(previous)..addAll(values['objects']); + } else { + throw 'Unable to add an item to a non-array.'; + } + } + if (previousAction == 'Add') { + if (values['objects'].length == 1) { + previous['objects'].add(values['objects'].first); + } else { + previous['objects'].add(values['objects']); + } + values = previous; + } + if (previousAction == 'Increment') { + throw 'Add operation is invalid after Increment operation'; + } + if (previousAction == 'Remove') { + throw 'Add operation is invalid after Remove operation'; + } + if (previousAction == 'AddUnique') { + throw 'Add operation is invalid after AddUnique operation'; + } + if (previousAction == 'AddRelation') { + throw 'Add operation is invalid after AddRelation operation'; + } + if (previousAction == 'RemoveRelation') { + throw 'Add operation is invalid after RemoveRelation operation'; + } + return values; + } + + // Remove operation Merge + dynamic _mergeWithPreviousRemove( + String previousAction, dynamic previous, dynamic values) { + if (previousAction == 'Set') { + return previous; + } + if (previousAction == 'Remove') { + if (values['objects'].length == 1) { + previous['objects'].add(values['objects'].first); + } else { + previous['objects'].add(values['objects']); + } + values = previous; + } + if (previousAction == 'Increment') { + throw 'Remove operation is invalid after Increment operation'; + } + if (previousAction == 'Add') { + throw 'Remove operation is invalid after Add operation'; + } + if (previousAction == 'AddUnique') { + throw 'Remove operation is invalid after AddUnique operation'; + } + if (previousAction == 'AddRelation') { + throw 'Remove operation is invalid after AddRelation operation'; + } + if (previousAction == 'RemoveRelation') { + throw 'Remove operation is invalid after RemoveRelation operation'; + } + return values; + } + + // Increment operation Merge + dynamic _mergeWithPreviousIncrement( + String previousAction, dynamic previous, dynamic values) { + if (previousAction == 'Set') { + if (previous is num) { + values['amount'] += previous; + } else { + throw 'Invalid Operation'; + } + } + if (previousAction == 'Increment') { + values['amount'] += previous['amount']; + } + if (previousAction == 'Add') { + throw 'Increment operation is invalid after Add operation'; + } + if (previousAction == 'Remove') { + throw 'Increment operation is invalid after Remove operation'; + } + if (previousAction == 'AddUnique') { + throw 'Increment operation is invalid after AddUnique operation'; + } + if (previousAction == 'AddRelation') { + throw 'Increment operation is invalid after AddRelation operation'; + } + if (previousAction == 'RemoveRelation') { + throw 'Increment operation is invalid after RemoveRelation operation'; + } + return values; + } + + // AddUnique operation Merge + dynamic _mergeWithPreviousAddUnique( + String previousAction, dynamic previous, dynamic values) { + if (previousAction == 'Set') { + if (previous is List) { + return _applyToValueAddUnique(previous, values['objects']); + } else { + throw 'Unable to add an item to a non-array.'; + } + } + if (previousAction == 'AddUnique') { + values['objects'] = + _applyToValueAddUnique(previous['objects'], values['objects']); + return values; + } + if (previousAction == 'Add') { + throw 'AddUnique operation is invalid after Add operation'; + } + if (previousAction == 'Remove') { + throw 'AddUnique operation is invalid after Reomve operation'; + } + if (previousAction == 'Increment') { + throw 'AddUnique operation is invalid after Increment operation'; + } + if (previousAction == 'AddRelation') { + throw 'AddUnique operation is invalid after AddRelation operation'; + } + if (previousAction == 'RemoveRelation') { + throw 'AddUnique operation is invalid after RemoveRelation operation'; + } + return values; + } + + // AddRelation operation Merge + dynamic _mergeWithPreviousAddRelation( + String previousAction, dynamic previous, dynamic values) { + if (previousAction == 'AddRelation') { + if (values['objects'].length == 1) { + previous['objects'].add(values['objects'].first); + } else { + previous['objects'].add(values['objects']); + } + values = previous; + } + if (previousAction == 'Set') { + throw 'AddRelation operation is invalid after Set operation.'; + } + if (previousAction == 'Increment') { + throw 'AddRelation operation is invalid after Increment operation'; + } + if (previousAction == 'Add') { + throw 'AddRelation operation is invalid after Add operation'; + } + if (previousAction == 'Remove') { + throw 'AddRelation operation is invalid after Remove operation'; + } + if (previousAction == 'AddUnique') { + throw 'AddRelation operation is invalid after AddUnique operation'; + } + if (previousAction == 'RemoveRelation') { + throw 'AddRelation operation is invalid after RemoveRelation operation'; + } + return values; + } + + // RemoveRelation operation Merge + dynamic _mergeWithPreviousRemoveRelation( + String previousAction, dynamic previous, dynamic values) { + if (previousAction == 'RemoveRelation') { + if (values['objects'].length == 1) { + previous['objects'].add(values['objects'].first); + } else { + previous['objects'].add(values['objects']); + } + values = previous; + } + if (previousAction == 'Set') { + throw 'RemoveRelation operation is invalid after Set operation.'; + } + if (previousAction == 'Increment') { + throw 'RemoveRelation operation is invalid after Increment operation'; + } + if (previousAction == 'Add') { + throw 'RemoveRelation operation is invalid after Add operation'; + } + if (previousAction == 'Remove') { + throw 'RemoveRelation operation is invalid after Remove operation'; + } + if (previousAction == 'AddUnique') { + throw 'RemoveRelation operation is invalid after AddUnique operation'; + } + if (previousAction == 'AddRelation') { + throw 'RemoveRelation operation is invalid after AddRelation operation'; + } + return values; + } + + // service for AddUnique method + dynamic _applyToValueAddUnique(dynamic oldValue, dynamic newValue) { + for (var objectToAdd in newValue) { + if (objectToAdd is ParseObject && objectToAdd.objectId != null) { + var index = 0; + for (var objc in oldValue) { + if (objc is ParseObject && objc.objectId == objectToAdd.objectId) { + oldValue[index] = objectToAdd; + break; + } + index += 1; + } + if (index == oldValue.length) { + oldValue.add(objectToAdd); + } + } else if (!oldValue.contains(objectToAdd)) { + oldValue.add(objectToAdd); + } + } + print(oldValue); + return oldValue; + } +} From d2b8cf3bcaa85d6fe931c56beee3cdbe0c822da6 Mon Sep 17 00:00:00 2001 From: moorelwang Date: Wed, 7 Aug 2019 16:02:54 +0800 Subject: [PATCH 24/25] remove the commented out code. --- lib/src/objects/parse_base.dart | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/src/objects/parse_base.dart b/lib/src/objects/parse_base.dart index 5218fb508..01e1535bb 100644 --- a/lib/src/objects/parse_base.dart +++ b/lib/src/objects/parse_base.dart @@ -191,12 +191,6 @@ abstract class ParseBase { if (_getObjectData()[key] == value) { return; } - /*if (!forceUpdate) { - if (value is Map) { - value = mergeWithPrevious(_getObjectData()[key], value); - } - }*/ - // value = mergeWithPrevious(_unsavedChanges[key], value); _getObjectData()[key] = ParseMergeTool().mergeWithPrevious(_unsavedChanges[key], value); } else { From 5a65098c0b0e322e8933436af782895c77f1e3e6 Mon Sep 17 00:00:00 2001 From: moorelwang Date: Fri, 9 Aug 2019 19:17:54 +0800 Subject: [PATCH 25/25] Fix OrderBy multiple columns #241 --- lib/src/network/parse_query.dart | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/src/network/parse_query.dart b/lib/src/network/parse_query.dart index 4c8b06266..2ac995bc9 100644 --- a/lib/src/network/parse_query.dart +++ b/lib/src/network/parse_query.dart @@ -32,7 +32,11 @@ class QueryBuilder { /// [String] order will be the column of the table that the results are /// ordered by void orderByAscending(String order) { - limiters['order'] = order; + if (limiters.containsKey('order')) { + limiters['order'] += ',$order'; + } else { + limiters['order'] = order; + } } /// Sorts the results descending order. @@ -40,7 +44,11 @@ class QueryBuilder { /// [String] order will be the column of the table that the results are /// ordered by void orderByDescending(String order) { - limiters['order'] = '-$order'; + if (limiters.containsKey('order')) { + limiters['order'] += ',-$order'; + } else { + limiters['order'] = '-$order'; + } } /// Define which keys in an object to return.