parse_server_sdk 1.0.20

  • README.md
  • CHANGELOG.md
  • Example
  • Installing
  • Versions
  • 92

enter image description here enter image description here

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).

This is a work in project and we are consistently updating it. Please let us know if you think anything needs changing/adding, and more than ever, please do join in on this project (Even if it is just to improve our documentation.

Join in! #

Want to get involved? Join our Slack channel and help out! (http://flutter-parse-sdk.slack.com)

Getting Started #

To install, either add to your pubspec.yaml

dependencies:  
    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.

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...

Parse().initialize(
        ApplicationConstants.keyApplicationId,
        ApplicationConstants.keyParseServerUrl);

It's possible to add other params, such as ...

Parse().initialize(
        ApplicationConstants.keyApplicationId,
        ApplicationConstants.keyParseServerUrl,
        masterKey: ApplicationConstants.keyParseMasterKey,
        clientKey: ApplicationConstants.keyParseClientKey,
        debug: true,
        liveQueryUrl: ApplicationConstants.keyLiveQueryUrl,
        autoSendSessionId: true,
        securityContext: securityContext);

Objects #

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

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
  • GetAll
  • Create
  • Save
  • Query - By object Id
  • Delete
  • Complex queries as shown above
  • Pin
  • Plenty more
  • Counters
  • Array Operators

Custom Objects #

You can create your own ParseObjects or convert your existing objects into Parse Objects by doing the following:

class DietPlan extends ParseObject implements ParseCloneable {

  DietPlan() : super(_keyTableName);
  DietPlan.clone(): this();

  /// Looks strangely hacky but due to Flutter not using reflection, we have to
  /// mimic a clone
  @override clone(Map map) => DietPlan.clone()..fromJson(map);

  static const String _keyTableName = 'Diet_Plans';
  static const String keyName = 'Name';
  
  String get name => get<String>(keyName);
  set name(String name) => set<String>(keyName, name);
}
  

Add new values to objects #

To add a variable to an object call and retrieve it, call

dietPlan.set<int>('RandomInt', 8);
var randomInt = dietPlan.get<int>('RandomInt');

Save objects using pins #

You can now save an object by calling .pin() on an instance of an object

dietPlan.pin();

and to retrieve it

var dietPlan = DietPlan().fromPin('OBJECT ID OF OBJECT');

Increment Counter values in objects #

Retrieve it, call

var response = await dietPlan.increment("count", 1);

or using with save function

dietPlan.setIncrement('count', 1);
dietPlan.setDecrement('count', 1);
var response = dietPlan.save()

Array Operator in objects #

Retrieve it, call

var response = await dietPlan.add("listKeywords", ["a", "a","d"]);

var response = await dietPlan.addUnique("listKeywords", ["a", "a","d"]);

var response = await dietPlan.remove("listKeywords", ["a"]);

or using with save function

dietPlan.setAdd('listKeywords', ['a','a','d']);
dietPlan.setAddUnique('listKeywords', ['a','a','d']);
dietPlan.setRemove('listKeywords', ['a']);
var response = dietPlan.save()

Queries #

Once you have setup the project and initialised the instance, you can then retreive data from your server by calling:

var apiResponse = await ParseObject('ParseTableName').getAll();

if (apiResponse.success){
  for (var testObject in apiResponse.result) {
    print(ApplicationConstants.APP_NAME + ": " + testObject.toString());
  }
}

Or you can get an object by its objectId:

var dietPlan = await DietPlan().getObject('R5EonpUDWy');

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:

var queryBuilder = QueryBuilder<DietPlan>(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<dynamic>).first as DietPlan).toString());
} else {
  print(ApplicationConstants.keyAppName + ": " + response.exception.message);
}

The features available are:-

  • Equals
  • Contains
  • LessThan
  • LessThanOrEqualTo
  • GreaterThan
  • GreaterThanOrEqualTo
  • NotEqualTo
  • StartsWith
  • EndsWith
  • Exists
  • Near
  • WithinMiles
  • WithinKilometers
  • WithinRadians
  • WithinGeoBox
  • Regex
  • Order
  • Limit
  • Skip
  • Ascending
  • Descending
  • Plenty more!

Relational queries #

If you want to retrieve objects where a field contains an object that matches another query, you can use the whereMatchesQuery condition. For example, imagine you have Post class and a Comment class, where each Comment has a pointer to its parent Post. You can find comments on posts with images by doing:

QueryBuilder<ParseObject> queryPost =
    QueryBuilder<ParseObject>(ParseObject('Post'))
      ..whereValueExists('image', true);

QueryBuilder<ParseObject> queryComment =
    QueryBuilder<ParseObject>(ParseObject('Comment'))
      ..whereMatchesQuery('post', queryPost);

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 whereDoesNotMatchQuery condition. Imagine you have Post class and a Comment class, where each Comment has a pointer to its parent Post. You can find comments on posts without images by doing:

QueryBuilder<ParseObject> queryPost =
    QueryBuilder<ParseObject>(ParseObject('Post'))
      ..whereValueExists('image', true);

QueryBuilder<ParseObject> queryComment =
    QueryBuilder<ParseObject>(ParseObject('Comment'))
      ..whereDoesNotMatchQuery('post', queryPost);

var apiResponse = await queryComment.query();

Counting Objects #

If you only care about the number of games played by a particular player:

QueryBuilder<ParseObject> queryPlayers =
    QueryBuilder<ParseObject>(ParseObject('GameScore'))
      ..whereEqualTo('playerName', 'Jonathan Walsh');
var apiResponse = await queryPlayers.count();
if (apiResponse.success && apiResponse.result != null) {
  int countGames = apiResponse.count;
}

Live Queries #

This tool allows you to subscribe to a QueryBuilder you are interested in. Once subscribed, the server will notify clients whenever a ParseObject that matches the QueryBuilder is created or updated, in real-time.

Parse LiveQuery contains two parts, the LiveQuery server and the LiveQuery clients. In order to use live queries, you need to set up both of them.

The Parse Server configuration guide on the server is found here https://docs.parseplatform.org/parse-server/guide/#live-queries and is not part of this documentation.

Initialize the Parse Live Query by entering the parameter liveQueryUrl in Parse().initialize:

Parse().initialize(
      ApplicationConstants.keyApplicationId,
      ApplicationConstants.keyParseServerUrl,
      clientKey: ApplicationConstants.keyParseClientKey,
      debug: true,
      liveQueryUrl: ApplicationConstants.keyLiveQueryUrl,
      autoSendSessionId: true);

Declare LiveQuery:

final LiveQuery liveQuery = LiveQuery();

Set the QueryBuilder that will be monitored by LiveQuery:

QueryBuilder<ParseObject> query =
  QueryBuilder<ParseObject>(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.

await liveQuery.subscribe(query);

Event Handling We define several types of events you’ll get through a subscription object:

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.

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 When an existing ParseObject which fulfills the QueryBuilder you subscribe is updated (The ParseObject fulfills the 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.

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 When an existing ParseObject’s old value does not fulfill the QueryBuilder but its new value fulfills the QueryBuilder, you’ll get this event. The object is the ParseObject which enters the QueryBuilder. Its content is the latest value of the ParseObject.

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 When an existing ParseObject’s old value fulfills the QueryBuilder but its new value doesn’t fulfill the QueryBuilder, you’ll get this event. The object is the ParseObject which leaves the QueryBuilder. Its content is the latest value of the ParseObject.

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

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 If you would like to stop receiving events from a QueryBuilder, you can just unsubscribe the subscription. After that, you won’t get any events from the subscription object and will close the WebSocket connection to the LiveQuery server.

await liveQuery.unSubscribe();

Users #

You can create and control users just as normal using this SDK.

To register a user, first create one :

var user =  ParseUser().create("TestFlutter", "TestPassword123", "TestFlutterSDK@gmail.com");

Then have the user sign up:

var response = await user.signUp();
if (response.success) user = response.result;

You can also login with the user:

var response = await user.login();
if (response.success) user = response.result;

You can also logout with the user:

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.

user = ParseUser.currentUser();

Other user features are:-

  • Request Password Reset
  • Verification Email Request
  • Get all users
  • Save
  • 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.

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

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.

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:

ParseACL parseACL = parseObject.getACL();

Config #

The SDK supports Parse Config. A map of all configs can be grabbed from the server by calling :

var response = await ParseConfig().getConfigs();

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

final ParseCloudFunction function = ParseCloudFunction('hello');
final ParseResponse result =
    await function.executeObjectFunction<ParseObject>();
if (result.success) {
  if (result.result is ParseObject) {
    final ParseObject parseObject = result.result;
    print(parseObject.className);
  }
}

Executes a cloud function with parameters

final ParseCloudFunction function = ParseCloudFunction('hello');
final Map<String, String> params = <String, String>{'plan': 'paid'};
function.execute(parameters: params);

Other Features of this library #

Main:

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

  • Queries
  • Anonymous (View the example application)
  • 3rd Party Authentication

Objects:

  • Create new object
  • Extend Parse Object and create local objects that can be saved and retreived
  • Queries

Author:- #

This project was authored by Phill Wiggins. You can contact me at phill.wiggins@gmail.com

1.0.20 #

ACL now working emailVerified

1.0.19 #

Bug fix

1.0.18 #

Bug fix

1.0.17 #

LiveQuery fix Bug fixes

1.0.16 #

Bug fixes Fixed object delete Added port support

1.0.15 #

Fixed 'full' bool issue

1.0.14 #

Corrected delete & path issue Added Geo queries Added ability to add login oAuth data

1.0.13 #

Added full bool to convert objects to JSON correctly

1.0.12 #

Fixed logout

1.0.11 #

ParseFile fixed Anonymous login SecurityContext CloudFunctions with objects

1.0.10 #

Add ParseConfig. Fixed whereEqualsTo('', PARSEOBJECT) and other queries

1.0.9 #

Fixed Health Check issue

1.0.8 #

Fixed some queries

1.0.7 #

Some items now return a response rather than a ParseObject

1.0.6 #

BREAK FIX - Fixed ParseUser return type so now returns ParseResponse BREAK FIX - Changed query names to make more human readable Fixed pinning and unpinning

1.0.5 #

Corrected save. Now formatted items correctly for saving on server

1.0.4 #

Bug fix for get all items Can now pin/unpin/fromPin for all ParseObjects Now supports generics Cody tidy around extending

1.0.3 #

Added persistent storage. When a logged in user closes the app, then reopens, the data will now be persistent. Best practice would be to Parse.init, then Parse.currentUser. This will return the current user session and allow auto login. Can also pin data in storage.

1.0.2 #

Fixed login

1.0.1 #

Added documentation and GeoPoints

1.0.0 #

First full release!

0.0.4 #

Added description

0.0.3 #

Added more cloud functions

example/README.md

flutter_plugin_example #

Demonstrates how to use the flutter_plugin plugin.

Getting Started #

For help getting started with Flutter, view our online documentation.

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  parse_server_sdk: ^1.0.20

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter packages get

Alternatively, your editor might support flutter packages get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:parse_server_sdk/parse_server_sdk.dart';
  
Version Uploaded Documentation Archive
1.0.20 Apr 22, 2019 Go to the documentation of parse_server_sdk 1.0.20 Download parse_server_sdk 1.0.20 archive
1.0.19 Apr 14, 2019 Go to the documentation of parse_server_sdk 1.0.19 Download parse_server_sdk 1.0.19 archive
1.0.18 Apr 14, 2019 Go to the documentation of parse_server_sdk 1.0.18 Download parse_server_sdk 1.0.18 archive
1.0.17 Apr 14, 2019 Go to the documentation of parse_server_sdk 1.0.17 Download parse_server_sdk 1.0.17 archive
1.0.16 Mar 20, 2019 Go to the documentation of parse_server_sdk 1.0.16 Download parse_server_sdk 1.0.16 archive
1.0.15 Mar 7, 2019 Go to the documentation of parse_server_sdk 1.0.15 Download parse_server_sdk 1.0.15 archive
1.0.14 Mar 6, 2019 Go to the documentation of parse_server_sdk 1.0.14 Download parse_server_sdk 1.0.14 archive
1.0.13 Mar 3, 2019 Go to the documentation of parse_server_sdk 1.0.13 Download parse_server_sdk 1.0.13 archive
1.0.12 Feb 16, 2019 Go to the documentation of parse_server_sdk 1.0.12 Download parse_server_sdk 1.0.12 archive
1.0.11 Feb 7, 2019 Go to the documentation of parse_server_sdk 1.0.11 Download parse_server_sdk 1.0.11 archive

All 25 versions...

Popularity:
Describes how popular the package is relative to other packages. [more]
85
Health:
Code health derived from static analysis. [more]
98
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
92
Learn more about scoring.

We analyzed this package on Apr 23, 2019, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.2.0
  • pana: 0.12.14
  • Flutter: 1.4.7

Platforms

Detected platforms: Flutter

References Flutter, and has no conflicting libraries.

Health suggestions

Fix lib/src/network/parse_live_query.dart. (-1 points)

Analysis of lib/src/network/parse_live_query.dart reported 2 hints:

line 77 col 49: Avoid using braces in interpolation when not needed.

line 170 col 9: Await only futures.

Fix lib/src/objects/parse_installation.dart. (-1 points)

Analysis of lib/src/objects/parse_installation.dart reported 2 hints:

line 195 col 5: 'addUnique' is deprecated and shouldn't be used.

line 201 col 5: 'removeAll' is deprecated and shouldn't be used.

Format lib/parse_server_sdk.dart.

Run flutter format to format lib/parse_server_sdk.dart.

Fix additional 14 files with analysis or formatting issues.

Additional issues in the following files:

  • lib/src/data/parse_core_data.dart (Run flutter format to format lib/src/data/parse_core_data.dart.)
  • lib/src/objects/parse_error.dart (Run flutter format to format lib/src/objects/parse_error.dart.)
  • lib/src/objects/parse_file.dart (Run flutter format to format lib/src/objects/parse_file.dart.)
  • lib/src/objects/parse_function.dart (Run flutter format to format lib/src/objects/parse_function.dart.)
  • lib/src/objects/parse_geo_point.dart (Run flutter format to format lib/src/objects/parse_geo_point.dart.)
  • lib/src/objects/parse_object.dart (Run flutter format to format lib/src/objects/parse_object.dart.)
  • lib/src/objects/response/parse_error_response.dart (Run flutter format to format lib/src/objects/response/parse_error_response.dart.)
  • lib/src/objects/response/parse_exception_response.dart (Run flutter format to format lib/src/objects/response/parse_exception_response.dart.)
  • lib/src/objects/response/parse_response_utils.dart (Run flutter format to format lib/src/objects/response/parse_response_utils.dart.)
  • lib/src/objects/response/parse_success_no_results.dart (Run flutter format to format lib/src/objects/response/parse_success_no_results.dart.)
  • lib/src/utils/parse_date_format.dart (Run flutter format to format lib/src/utils/parse_date_format.dart.)
  • lib/src/utils/parse_decoder.dart (Run flutter format to format lib/src/utils/parse_decoder.dart.)
  • lib/src/utils/parse_logger.dart (Run flutter format to format lib/src/utils/parse_logger.dart.)
  • lib/src/utils/parse_utils.dart (Run flutter format to format lib/src/utils/parse_utils.dart.)

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.0.0-dev.68.0 <3.0.0
devicelocale ^0.1.1 0.1.1
flutter 0.0.0
http ^0.12.0 0.12.0+2
package_info ^0.4.0 0.4.0+3
path_provider ^0.5.0+1 0.5.0+1
shared_preferences ^0.5.0 0.5.2
uuid ^2.0.0 2.0.1
web_socket_channel ^1.0.9 1.0.12
Transitive dependencies
async 2.2.0
charcode 1.1.2
collection 1.14.11
convert 2.1.1
crypto 2.0.6
http_parser 3.1.3
meta 1.1.6 1.1.7
path 1.6.2
pedantic 1.5.0
sky_engine 0.0.99
source_span 1.5.5
stream_channel 2.0.0
string_scanner 1.0.4
term_glyph 1.1.0
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
flutter_test
test ^1.5.1