diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a0f72dfd..6f112b713 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.20 +ACL now working +emailVerified + ## 1.0.19 Bug fix diff --git a/README.md b/README.md index 7c0f66bb3..2f080a3e1 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,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.19 + parse_server_sdk: ^1.0.20 ``` 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. @@ -46,7 +46,27 @@ You can create custom objects by calling: var dietPlan = ParseObject('DietPlan') ..set('Name', 'Ketogenic') ..set('Fat', 65); +await dietPlan.save() ``` +Verify that the object has been successfully saved using +```dart +var response = await dietPlan.save(); +if (response.success) { + dietPlan = response.result; +} +``` +Types supported: + * String + * Double + * Int + * Boolean + * DateTime + * File + * Geopoint + * ParseObject/ParseUser (Pointer) + * Map + * List (all types supported) + You then have the ability to do the following with that object: The features available are:- * Get @@ -110,6 +130,14 @@ Retrieve it, call ```dart var response = await dietPlan.increment("count", 1); +``` +or using with save function + +```dart +dietPlan.setIncrement('count', 1); +dietPlan.setDecrement('count', 1); +var response = dietPlan.save() + ``` ## Array Operator in objects @@ -122,6 +150,14 @@ var response = await dietPlan.addUnique("listKeywords", ["a", "a","d"]); var response = await dietPlan.remove("listKeywords", ["a"]); +``` +or using with save function + +```dart +dietPlan.setAdd('listKeywords', ['a','a','d']); +dietPlan.setAddUnique('listKeywords', ['a','a','d']); +dietPlan.setRemove('listKeywords', ['a']); +var response = dietPlan.save() ``` ## Queries @@ -129,41 +165,41 @@ Once you have setup the project and initialised the instance, you can then retre ```dart var apiResponse = await ParseObject('ParseTableName').getAll(); - if (apiResponse.success){ - for (var testObject in apiResponse.result) { - print(ApplicationConstants.APP_NAME + ": " + testObject.toString()); - } - } +if (apiResponse.success){ + for (var testObject in apiResponse.result) { + print(ApplicationConstants.APP_NAME + ": " + testObject.toString()); + } +} ``` Or you can get an object by its objectId: ```dart var dietPlan = await DietPlan().getObject('R5EonpUDWy'); - if (dietPlan.success) { - print(ApplicationConstants.keyAppName + ": " + (dietPlan.result as DietPlan).toString()); - } else { - print(ApplicationConstants.keyAppName + ": " + dietPlan.exception.message); - } +if (dietPlan.success) { + print(ApplicationConstants.keyAppName + ": " + (dietPlan.result as DietPlan).toString()); +} else { + print(ApplicationConstants.keyAppName + ": " + dietPlan.exception.message); +} ``` ## Complex queries You can create complex queries to really put your database to the test: ```dart - var queryBuilder = QueryBuilder(DietPlan()) - ..startsWith(DietPlan.keyName, "Keto") - ..greaterThan(DietPlan.keyFat, 64) - ..lessThan(DietPlan.keyFat, 66) - ..equals(DietPlan.keyCarbs, 5); - - var response = await queryBuilder.query(); - - if (response.success) { - print(ApplicationConstants.keyAppName + ": " + ((response.result as List).first as DietPlan).toString()); - } else { - print(ApplicationConstants.keyAppName + ": " + response.exception.message); - } +var queryBuilder = QueryBuilder(DietPlan()) + ..startsWith(DietPlan.keyName, "Keto") + ..greaterThan(DietPlan.keyFat, 64) + ..lessThan(DietPlan.keyFat, 66) + ..equals(DietPlan.keyCarbs, 5); + +var response = await queryBuilder.query(); + +if (response.success) { + print(ApplicationConstants.keyAppName + ": " + ((response.result as List).first as DietPlan).toString()); +} else { + print(ApplicationConstants.keyAppName + ": " + response.exception.message); +} ``` The features available are:- @@ -197,15 +233,15 @@ For example, imagine you have Post class and a Comment class, where each Comment You can find comments on posts with images by doing: ```dart - QueryBuilder queryPost = - QueryBuilder(ParseObject('Post')) - ..whereValueExists('image', true); +QueryBuilder queryPost = + QueryBuilder(ParseObject('Post')) + ..whereValueExists('image', true); - QueryBuilder queryComment = - QueryBuilder(ParseObject('Comment')) - ..whereMatchesQuery('post', queryPost); +QueryBuilder queryComment = + QueryBuilder(ParseObject('Comment')) + ..whereMatchesQuery('post', queryPost); - var apiResponse = await queryComment.query(); +var apiResponse = await queryComment.query(); ``` If you want to retrieve objects where a field contains an object that does not match another query, you can use the @@ -214,28 +250,28 @@ Imagine you have Post class and a Comment class, where each Comment has a pointe You can find comments on posts without images by doing: ```dart - QueryBuilder queryPost = - QueryBuilder(ParseObject('Post')) - ..whereValueExists('image', true); +QueryBuilder queryPost = + QueryBuilder(ParseObject('Post')) + ..whereValueExists('image', true); - QueryBuilder queryComment = - QueryBuilder(ParseObject('Comment')) - ..whereDoesNotMatchQuery('post', queryPost); +QueryBuilder queryComment = + QueryBuilder(ParseObject('Comment')) + ..whereDoesNotMatchQuery('post', queryPost); - var apiResponse = await queryComment.query(); +var apiResponse = await queryComment.query(); ``` ## Counting Objects If you only care about the number of games played by a particular player: ```dart - QueryBuilder queryPlayers = - QueryBuilder(ParseObject('GameScore')) - ..whereEqualTo('playerName', 'Jonathan Walsh'); - var apiResponse = await queryPlayers.count(); - if (apiResponse.success && apiResponse.result != null) { - int countGames = apiResponse.count; - } +QueryBuilder queryPlayers = + QueryBuilder(ParseObject('GameScore')) + ..whereEqualTo('playerName', 'Jonathan Walsh'); +var apiResponse = await queryPlayers.count(); +if (apiResponse.success && apiResponse.result != null) { + int countGames = apiResponse.count; +} ``` ## Live Queries @@ -249,32 +285,32 @@ 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, - debug: true, - liveQueryUrl: ApplicationConstants.keyLiveQueryUrl, - autoSendSessionId: true); +Parse().initialize( + ApplicationConstants.keyApplicationId, + ApplicationConstants.keyParseServerUrl, + clientKey: ApplicationConstants.keyParseClientKey, + debug: true, + liveQueryUrl: ApplicationConstants.keyLiveQueryUrl, + autoSendSessionId: true); ``` Declare LiveQuery: ```dart - final LiveQuery liveQuery = LiveQuery(); +final LiveQuery liveQuery = LiveQuery(); ``` Set the QueryBuilder that will be monitored by LiveQuery: ```dart - QueryBuilder query = - QueryBuilder(ParseObject('TestAPI')) - ..whereEqualTo('intNumber', 1); +QueryBuilder query = + QueryBuilder(ParseObject('TestAPI')) + ..whereEqualTo('intNumber', 1); ``` __Create a subscription__ You’ll get the LiveQuery events through this subscription. The first time you call subscribe, we’ll try to open the WebSocket connection to the LiveQuery server for you. ```dart - await liveQuery.subscribe(query); +await liveQuery.subscribe(query); ``` __Event Handling__ @@ -284,15 +320,15 @@ __Create event__ When a new ParseObject is created and it fulfills the QueryBuilder you subscribe, you’ll get this event. The object is the ParseObject which was created. ```dart - liveQuery.on(LiveQueryEvent.create, (value) { - print('*** CREATE ***: ${DateTime.now().toString()}\n $value '); - print((value as ParseObject).objectId); - print((value as ParseObject).updatedAt); - print((value as ParseObject).createdAt); - print((value as ParseObject).get('objectId')); - print((value as ParseObject).get('updatedAt')); - print((value as ParseObject).get('createdAt')); - }); +liveQuery.on(LiveQueryEvent.create, (value) { + print('*** CREATE ***: ${DateTime.now().toString()}\n $value '); + print((value as ParseObject).objectId); + print((value as ParseObject).updatedAt); + print((value as ParseObject).createdAt); + print((value as ParseObject).get('objectId')); + print((value as ParseObject).get('updatedAt')); + print((value as ParseObject).get('createdAt')); +}); ``` __Update event__ @@ -300,15 +336,15 @@ When an existing ParseObject which fulfills the QueryBuilder you subscribe is up QueryBuilder before and after changes), you’ll get this event. The object is the ParseObject which was updated. Its content is the latest value of the ParseObject. ```dart - liveQuery.on(LiveQueryEvent.update, (value) { - print('*** UPDATE ***: ${DateTime.now().toString()}\n $value '); - print((value as ParseObject).objectId); - print((value as ParseObject).updatedAt); - print((value as ParseObject).createdAt); - print((value as ParseObject).get('objectId')); - print((value as ParseObject).get('updatedAt')); - print((value as ParseObject).get('createdAt')); - }); +liveQuery.on(LiveQueryEvent.update, (value) { + print('*** UPDATE ***: ${DateTime.now().toString()}\n $value '); + print((value as ParseObject).objectId); + print((value as ParseObject).updatedAt); + print((value as ParseObject).createdAt); + print((value as ParseObject).get('objectId')); + print((value as ParseObject).get('updatedAt')); + print((value as ParseObject).get('createdAt')); +}); ``` __Enter event__ @@ -316,15 +352,15 @@ When an existing ParseObject’s old value does not fulfill the QueryBuilder but you’ll get this event. The object is the ParseObject which enters the QueryBuilder. Its content is the latest value of the ParseObject. ```dart - liveQuery.on(LiveQueryEvent.enter, (value) { - print('*** ENTER ***: ${DateTime.now().toString()}\n $value '); - print((value as ParseObject).objectId); - print((value as ParseObject).updatedAt); - print((value as ParseObject).createdAt); - print((value as ParseObject).get('objectId')); - print((value as ParseObject).get('updatedAt')); - print((value as ParseObject).get('createdAt')); - }); +liveQuery.on(LiveQueryEvent.enter, (value) { + print('*** ENTER ***: ${DateTime.now().toString()}\n $value '); + print((value as ParseObject).objectId); + print((value as ParseObject).updatedAt); + print((value as ParseObject).createdAt); + print((value as ParseObject).get('objectId')); + print((value as ParseObject).get('updatedAt')); + print((value as ParseObject).get('createdAt')); +}); ``` __Leave event__ @@ -332,30 +368,30 @@ When an existing ParseObject’s old value fulfills the QueryBuilder but its new you’ll get this event. The object is the ParseObject which leaves the QueryBuilder. Its content is the latest value of the ParseObject. ```dart - liveQuery.on(LiveQueryEvent.leave, (value) { - print('*** LEAVE ***: ${DateTime.now().toString()}\n $value '); - print((value as ParseObject).objectId); - print((value as ParseObject).updatedAt); - print((value as ParseObject).createdAt); - print((value as ParseObject).get('objectId')); - print((value as ParseObject).get('updatedAt')); - print((value as ParseObject).get('createdAt')); - }); +liveQuery.on(LiveQueryEvent.leave, (value) { + print('*** LEAVE ***: ${DateTime.now().toString()}\n $value '); + print((value as ParseObject).objectId); + print((value as ParseObject).updatedAt); + print((value as ParseObject).createdAt); + print((value as ParseObject).get('objectId')); + print((value as ParseObject).get('updatedAt')); + print((value as ParseObject).get('createdAt')); +}); ``` __Delete event__ When an existing ParseObject which fulfills the QueryBuilder is deleted, you’ll get this event. The object is the ParseObject which is deleted ```dart - liveQuery.on(LiveQueryEvent.delete, (value) { - print('*** DELETE ***: ${DateTime.now().toString()}\n $value '); - print((value as ParseObject).objectId); - print((value as ParseObject).updatedAt); - print((value as ParseObject).createdAt); - print((value as ParseObject).get('objectId')); - print((value as ParseObject).get('updatedAt')); - print((value as ParseObject).get('createdAt')); - }); +liveQuery.on(LiveQueryEvent.delete, (value) { + print('*** DELETE ***: ${DateTime.now().toString()}\n $value '); + print((value as ParseObject).objectId); + print((value as ParseObject).updatedAt); + print((value as ParseObject).createdAt); + print((value as ParseObject).get('objectId')); + print((value as ParseObject).get('updatedAt')); + print((value as ParseObject).get('createdAt')); +}); ``` __Unsubscribe__ @@ -364,7 +400,7 @@ After that, you won’t get any events from the subscription object and will clo LiveQuery server. ```dart - await liveQuery.unSubscribe(); +await liveQuery.unSubscribe(); ``` ## Users @@ -380,11 +416,18 @@ Then have the user sign up: var response = await user.signUp(); if (response.success) user = response.result; ``` -You can also logout and login with the user: +You can also login with the user: ```dart var response = await user.login(); if (response.success) user = response.result; ``` +You can also logout with the user: +```dart +var response = await user.logout(); +if (response.success) { + print('User logout'); +} +``` Also, once logged in you can manage sessions tokens. This feature can be called after Parse().init() on startup to check for a logged in user. ```dart user = ParseUser.currentUser(); @@ -397,8 +440,64 @@ Other user features are:- * Destroy user * Queries +## 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. +To support this type of security, each object has an access control list, implemented by the __ParseACL__ class. + +If ParseACL is not specified (with the exception of the ParseUser class) all objects are set to Public for read and write. +The simplest way to use a ParseACL is to specify that an object may only be read or written by a single user. +To create such an object, there must first be a logged in ParseUser. Then, new ParseACL(user) generates a ParseACL that +limits access to that user. An object’s ACL is updated when the object is saved, like any other property. + +```dart +ParseUser user = await ParseUser.currentUser() as ParseUser; +ParseACL parseACL = ParseACL(owner: user); + +ParseObject parseObject = ParseObject("TestAPI"); +... +parseObject.setACL(parseACL); +var apiResponse = await parseObject.save(); +``` +Permissions can also be granted on a per-user basis. You can add permissions individually to a ParseACL using +__setReadAccess__ and __setWriteAccess__ +```dart +ParseUser user = await ParseUser.currentUser() as ParseUser; +ParseACL parseACL = ParseACL(); +//grant total access to current user +parseACL.setReadAccess(userId: user.objectId, allowed: true); +parseACL.setWriteAccess(userId: user.objectId, allowed: true); +//grant read access to userId: 'TjRuDjuSAO' +parseACL.setReadAccess(userId: 'TjRuDjuSAO', allowed: true); +parseACL.setWriteAccess(userId: 'TjRuDjuSAO', allowed: false); + +ParseObject parseObject = ParseObject("TestAPI"); +... +parseObject.setACL(parseACL); +var apiResponse = await parseObject.save(); +``` +You can also grant permissions to all users at once using setPublicReadAccess and setPublicWriteAccess. +```dart +ParseACL parseACL = ParseACL(); +parseACL.setPublicReadAccess(allowed: true); +parseACL.setPublicWriteAccess(allowed: true); + +ParseObject parseObject = ParseObject("TestAPI"); +... +parseObject.setACL(parseACL); +var apiResponse = await parseObject.save(); +``` +Operations that are forbidden, such as deleting an object that you do not have write access to, result in a +ParseError with code 101: 'ObjectNotFound'. +For security purposes, this prevents clients from distinguishing which object ids exist but are secured, versus which +object ids do not exist at all. + +You can retrieve the ACL list of an object using: +```dart +ParseACL parseACL = parseObject.getACL(); +``` + ## Config -The SDK now supports Parse Config. A map of all configs can be grabbed from the server by calling : +The SDK supports Parse Config. A map of all configs can be grabbed from the server by calling : ```dart var response = await ParseConfig().getConfigs(); ``` @@ -408,27 +507,41 @@ and to add a config: ParseConfig().addConfig('TestConfig', 'testing'); ``` +## Cloud Functions +The SDK supports call Cloud Functions. + +Executes a cloud function that returns a ParseObject type +```dart +final ParseCloudFunction function = ParseCloudFunction('hello'); +final ParseResponse result = + await function.executeObjectFunction(); +if (result.success) { + if (result.result is ParseObject) { + final ParseObject parseObject = result.result; + print(parseObject.className); + } +} +``` + +Executes a cloud function with parameters +```dart +final ParseCloudFunction function = ParseCloudFunction('hello'); +final Map params = {'plan': 'paid'}; +function.execute(parameters: params); +``` + ## Other Features of this library Main: -* Installation -* GeoPoints -* Files +* Installation (View the example application) +* GeoPoints (View the example application) +* Files (View the example application) * Persistent storage * Debug Mode - Logging API calls * Manage Session ID's tokens User: -* Create -* Login -* Logout -* CurrentUser -* RequestPasswordReset -* VerificationEmailRequest -* AllUsers -* Save -* Destroy * Queries -* Anonymous +* Anonymous (View the example application) * 3rd Party Authentication Objects: diff --git a/example/lib/main.dart b/example/lib/ui/main.dart similarity index 99% rename from example/lib/main.dart rename to example/lib/ui/main.dart index b5426b9a0..0e127bd16 100644 --- a/example/lib/main.dart +++ b/example/lib/ui/main.dart @@ -65,11 +65,11 @@ class _MyAppState extends State { Future runTestQueries() async { // Basic repository example await repositoryAddUser(); - /*await repositoryAddItems(); - await repositoryGetAllItems()*/ + await repositoryAddItems(); + await repositoryGetAllItems(); //Basic usage - /*createItem(); + createItem(); getAllItems(); getAllItemsByName(); getSingleItem(); @@ -78,7 +78,7 @@ class _MyAppState extends State { initUser(); function(); functionWithParameters(); - test();*/ + test(); } Future test() async { diff --git a/lib/parse_server_sdk.dart b/lib/parse_server_sdk.dart index 0ec5bcf6e..5541d9a94 100644 --- a/lib/parse_server_sdk.dart +++ b/lib/parse_server_sdk.dart @@ -62,6 +62,8 @@ part 'src/objects/parse_session.dart'; part 'src/objects/parse_user.dart'; +part 'src/objects/parse_acl.dart'; + part 'src/utils/parse_decoder.dart'; part 'src/utils/parse_encoder.dart'; diff --git a/lib/src/objects/parse_acl.dart b/lib/src/objects/parse_acl.dart new file mode 100644 index 000000000..2d3ff2e5e --- /dev/null +++ b/lib/src/objects/parse_acl.dart @@ -0,0 +1,149 @@ +part of flutter_parse_sdk; + +/// [ParseACL] is used to control which users can access or modify a particular object +/// [ParseObject] can have its own [ParceACL] +/// You can grant read and write permissions separately to specific users +/// or you can grant permissions to "the public" so that, for example, any user could read a particular object but +/// only a particular set of users could write to that object +class ParseACL { + ///Creates an ACL where only the provided user has access. + ///[owner] The only user that can read or write objects governed by this ACL. + ParseACL({ParseUser owner}) { + if (owner != null) { + setReadAccess(userId: owner.objectId, allowed: true); + setWriteAccess(userId: owner.objectId, allowed: true); + } + } + + final String _publicKEY = '*'; + final Map _permissionsById = {}; + + /// Helper for setting stuff + void _setPermissionsIfNonEmpty( + {@required String userId, bool readPermission, bool writePermission}) { + if (!(readPermission || writePermission)) { + _permissionsById.remove(userId); + } else { + _permissionsById[userId] = + _ACLPermissions(readPermission, writePermission); + } + } + + ///Get whether the public is allowed to read this object. + bool getPublicReadAccess() { + return getReadAccess(userId: _publicKEY); + } + + ///Set whether the public is allowed to read this object. + void setPublicReadAccess({@required bool allowed}) { + setReadAccess(userId: _publicKEY, allowed: allowed); + } + + /// Set whether the public is allowed to write this object. + bool getPublicWriteAccess() { + return getWriteAccess(userId: _publicKEY); + } + + ///Set whether the public is allowed to write this object. + void setPublicWriteAccess({@required bool allowed}) { + setWriteAccess(userId: _publicKEY, allowed: allowed); + } + + ///Set whether the given user id is allowed to read this object. + void setReadAccess({@required String userId, bool allowed}) { + if (userId == null) { + throw 'cannot setReadAccess for null userId'; + } + final bool writePermission = getWriteAccess(userId: userId); + _setPermissionsIfNonEmpty( + userId: userId, + readPermission: allowed, + writePermission: writePermission); + } + + /// Get whether the given user id is *explicitly* allowed to read this object. Even if this returns + /// [false], the user may still be able to access it if getPublicReadAccess returns + /// [true] or a role that the user belongs to has read access. + bool getReadAccess({@required String userId}) { + if (userId == null) { + throw 'cannot getReadAccess for null userId'; + } + final _ACLPermissions _permissions = _permissionsById[userId]; + return _permissions != null && _permissions.getReadPermission(); + } + + ///Set whether the given user id is allowed to write this object. + void setWriteAccess({@required String userId, bool allowed}) { + if (userId == null) { + throw 'cannot setWriteAccess for null userId'; + } + final bool readPermission = getReadAccess(userId: userId); + _setPermissionsIfNonEmpty( + userId: userId, + readPermission: readPermission, + writePermission: allowed); + } + + ///Get whether the given user id is *explicitly* allowed to write this object. Even if this + ///returns [false], the user may still be able to write it if getPublicWriteAccess returns + ///[true] or a role that the user belongs to has write access. + bool getWriteAccess({@required String userId}) { + if (userId == null) { + throw 'cannot getWriteAccess for null userId'; + } + final _ACLPermissions _permissions = _permissionsById[userId]; + return _permissions != null && _permissions.getReadPermission(); + } + + Map toJson() { + final Map map = {}; + _permissionsById.forEach((String user, _ACLPermissions permission) { + map[user] = permission.toJson(); + }); + return map; + } + + @override + String toString() => json.encode(toJson()); + + ParseACL fromJson(Map map) { + final ParseACL parseACL = ParseACL(); + + map.forEach((String userId, dynamic permission) { + if (permission['read'] != null) { + parseACL.setReadAccess(userId: userId, allowed: permission['read']); + } + if (permission['write'] != null) { + parseACL.setWriteAccess(userId: userId, allowed: permission['write']); + } + }); + return parseACL; + } +} + +class _ACLPermissions { + _ACLPermissions(this._readPermission, this._writePermission); + final String _keyReadPermission = 'read'; + final String _keyWritePermission = 'write'; + bool _readPermission = false; + bool _writePermission = false; + + bool getReadPermission() { + return _readPermission; + } + + bool getWritePermission() { + return _writePermission; + } + + Map toJson() { + final Map map = {}; + if (_readPermission) { + map[_keyReadPermission] = true; + } + if (_writePermission) { + map[_keyWritePermission] = true; + } + return map; + } +} diff --git a/lib/src/objects/parse_base.dart b/lib/src/objects/parse_base.dart index 2cdd579db..906f40ee2 100644 --- a/lib/src/objects/parse_base.dart +++ b/lib/src/objects/parse_base.dart @@ -65,7 +65,7 @@ abstract class ParseBase { map.remove(keyVarCreatedAt); map.remove(keyVarUpdatedAt); map.remove(keyVarClassName); - map.remove(keyVarAcl); + //map.remove(keyVarAcl); map.remove(keyParamSessionToken); } @@ -97,6 +97,8 @@ abstract class ParseBase { } else { set(keyVarUpdatedAt, value); } + } else if (key == keyVarAcl) { + getObjectData()[keyVarAcl] = ParseACL().fromJson(value); } else { getObjectData()[key] = parseDecode(value); } @@ -142,6 +144,20 @@ abstract class ParseBase { } } + ///Set the [ParseACL] governing this object. + void setACL(ParseACL acl) { + getObjectData()[keyVarAcl] = acl; + } + + ///Access the [ParseACL] governing this object. + ParseACL getACL() { + if (getObjectData().containsKey(keyVarAcl)) { + return getObjectData()[keyVarAcl]; + } else { + return ParseACL(); + } + } + /// Gets type [T] from objectData /// /// Returns null or [defaultValue] if provided. To get an int, call diff --git a/lib/src/objects/parse_object.dart b/lib/src/objects/parse_object.dart index 113b19c82..6b474fb9a 100644 --- a/lib/src/objects/parse_object.dart +++ b/lib/src/objects/parse_object.dart @@ -65,7 +65,7 @@ class ParseObject extends ParseBase implements ParseCloneable { Future create() async { try { final Uri url = getSanitisedUri(_client, '$_path'); - final String body = json.encode(toJson()); + final String body = json.encode(toJson(forApiRQ: true)); final Response result = await _client.post(url, body: body); //Set the objectId on the object after it is created. @@ -74,7 +74,7 @@ class ParseObject extends ParseBase implements ParseCloneable { final Map map = json.decode(result.body); objectId = map['objectId'].toString(); } - + return handleResponse( this, result, ParseApiRQ.create, _debug, className); } on Exception catch (e) { @@ -89,7 +89,7 @@ class ParseObject extends ParseBase implements ParseCloneable { } else { try { final Uri url = getSanitisedUri(_client, '$_path/$objectId'); - final String body = json.encode(toJson()); + final String body = json.encode(toJson(forApiRQ: true)); final Response result = await _client.put(url, body: body); return handleResponse( this, result, ParseApiRQ.save, _debug, className); diff --git a/lib/src/objects/parse_user.dart b/lib/src/objects/parse_user.dart index 2cc6a7d18..c4cb76f4d 100644 --- a/lib/src/objects/parse_user.dart +++ b/lib/src/objects/parse_user.dart @@ -36,6 +36,7 @@ class ParseUser extends ParseObject implements ParseCloneable { return fromJson(map); } + static const String keyEmailVerified = 'emailVerified'; static const String keyUsername = 'username'; static const String keyEmailAddress = 'email'; static const String path = '$keyEndPointClasses$keyClassUser'; @@ -45,6 +46,11 @@ class ParseUser extends ParseObject implements ParseCloneable { set acl(Map acl) => set>(keyVarAcl, acl); + bool get emailVerified => super.get(keyEmailVerified); + + set emailVerified(bool emailVerified) => + set(keyEmailVerified, emailVerified); + String get username => super.get(keyVarUsername); set username(String username) => set(keyVarUsername, username); @@ -108,7 +114,6 @@ class ParseUser extends ParseObject implements ParseCloneable { /// fromServer can be called and an updated version of the [User] object will be /// returned static Future currentUser({ParseCloneable customUserObject}) async { - if (customUserObject != null) { return await _getUserFromLocalStore(cloneable: customUserObject); } else { diff --git a/pubspec.yaml b/pubspec.yaml index a6346a0b0..2d8af20f5 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.19 +version: 1.0.20 homepage: https://github.com/phillwiggins/flutter_parse_sdk author: PhillWiggins