├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── lib ├── parse_server.dart └── src │ ├── parse_base.dart │ ├── parse_http_client.dart │ ├── parse_livequery.dart │ ├── parse_object.dart │ ├── parse_query.dart │ └── parse_user.dart ├── pubspec.yaml └── test └── parse_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | # Files and directories created by pub 2 | .packages 3 | .pub/ 4 | build/ 5 | # Remove the following pattern if you wish to check in your lock file 6 | pubspec.lock 7 | 8 | # Directory created by dartdoc 9 | doc/api/ 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.0.1 4 | 5 | - Initial version, created by Stagehand 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lotux/parse_server_dart/b52948ac8374611521ade7a22c75ac38efb9e058/LICENSE -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # parse_dart 2 | 3 | A Parse Server library for Dart developers. 4 | 5 | Created from templates made available by Stagehand under a BSD-style 6 | [license](https://github.com/dart-lang/stagehand/blob/master/LICENSE). 7 | 8 | ## Usage 9 | This is still WIP, API might change 10 | 11 | - User Authentication works 12 | - No query builder yet 13 | - LiveQuery websocket works 14 | - Parse Object works 15 | 16 | ### TODO: 17 | 18 | - Documentation 19 | - Query builder 20 | - ... 21 | 22 | 23 | A simple usage example: 24 | 25 | import 'package:parse_server/parse.dart'; 26 | 27 | main() { 28 | var awesome = new Parse(); 29 | } 30 | 31 | ## Features and bugs 32 | 33 | Please file feature requests and bugs at the [issue tracker][tracker]. 34 | 35 | [tracker]: https://github.com/lotux/parse_server_dart/issues 36 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | analyzer: 2 | strong-mode: true 3 | # exclude: 4 | # - path/to/excluded/files/** 5 | 6 | # Lint rules and documentation, see http://dart-lang.github.io/linter/lints 7 | linter: 8 | rules: 9 | - cancel_subscriptions 10 | - hash_and_equals 11 | - iterable_contains_unrelated_type 12 | - list_remove_unrelated_type 13 | - test_types_in_equals 14 | - unrelated_type_equality_checks 15 | - valid_regexps 16 | -------------------------------------------------------------------------------- /lib/parse_server.dart: -------------------------------------------------------------------------------- 1 | /// Support for doing something awesome. 2 | /// 3 | /// More dartdocs go here. 4 | library parse_server; 5 | 6 | export 'src/parse_base.dart'; 7 | export 'src/parse_livequery.dart'; 8 | export 'src/parse_object.dart'; 9 | export 'src/parse_query.dart'; 10 | export 'src/parse_user.dart'; 11 | 12 | // TODO: Export any libraries intended for clients of this package. 13 | -------------------------------------------------------------------------------- /lib/src/parse_base.dart: -------------------------------------------------------------------------------- 1 | import 'parse_object.dart'; 2 | import 'parse_user.dart'; 3 | import 'parse_livequery.dart'; 4 | import 'parse_http_client.dart'; 5 | 6 | class Credentials { 7 | final String applicationId; 8 | final String masterKey; 9 | String sessionId; 10 | 11 | Credentials(this.applicationId, [this.masterKey]); 12 | 13 | @override 14 | String toString() => "${applicationId}"; 15 | 16 | } 17 | 18 | abstract class ParseBaseObject { 19 | final String className; 20 | final ParseHTTPClient client; 21 | String path; 22 | Map objectData; 23 | 24 | String get objectId => objectData['objectId']; 25 | 26 | void _handleResponse(Map response){} 27 | 28 | ParseBaseObject(String this.className, [ParseHTTPClient this.client]); 29 | } 30 | 31 | 32 | class Parse { 33 | Credentials credentials; 34 | final String liveQueryServerURL; 35 | final String serverURL; 36 | final ParseHTTPClient client = new ParseHTTPClient(); 37 | 38 | Parse(Credentials credentials, String serverURL,[String liveQueryServerURL]) 39 | : credentials = credentials, 40 | serverURL = serverURL, 41 | liveQueryServerURL = liveQueryServerURL { 42 | client.baseURL = serverURL; 43 | client.liveQueryURL ??= liveQueryServerURL; 44 | client.credentials = credentials; 45 | } 46 | 47 | ParseObject _parseObject; 48 | User _user; 49 | LiveQuery _liveQuery; 50 | 51 | ParseObject object(objectName) { 52 | return _parseObject = new ParseObject(objectName, client); 53 | } 54 | 55 | User user() { 56 | return _user = new User(client); 57 | } 58 | LiveQuery liveQuery() { 59 | return _liveQuery = new LiveQuery(client); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /lib/src/parse_http_client.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:http/http.dart' as http; 3 | import 'package:http/src/streamed_response.dart'; 4 | import 'package:http/src/base_request.dart'; 5 | import 'parse_base.dart'; 6 | 7 | class ParseHTTPClient extends http.BaseClient { 8 | final http.Client _client = new http.Client(); 9 | final String _userAgent = "Dart Parse SDK 0.1"; 10 | String baseURL; 11 | String liveQueryURL; 12 | Credentials credentials; 13 | ParseHTTPClient(); 14 | 15 | 16 | @override 17 | Future send(BaseRequest request) { 18 | request.headers['user-agent'] = _userAgent; 19 | request.headers['X-Parse-Application-Id'] = credentials.applicationId; 20 | request.headers['Content-Type']= 'application/json'; 21 | return _client.send(request); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/src/parse_livequery.dart: -------------------------------------------------------------------------------- 1 | import "dart:convert"; 2 | import 'package:web_socket_channel/io.dart'; 3 | import 'parse_http_client.dart'; 4 | import 'dart:io'; 5 | 6 | class LiveQuery { 7 | final ParseHTTPClient client; 8 | var channel; 9 | Map connectMessage; 10 | Map subscribeMessage; 11 | Map eventCallbacks = {}; 12 | 13 | LiveQuery(ParseHTTPClient client) 14 | : client = client { 15 | connectMessage = { 16 | "op": "connect", 17 | "applicationId": client.credentials.applicationId, 18 | }; 19 | 20 | subscribeMessage = { 21 | "op": "subscribe", 22 | "requestId": 1, 23 | "query": { 24 | "className": null, 25 | "where": {}, 26 | } 27 | }; 28 | 29 | } 30 | 31 | subscribe(String className) async { 32 | //channel = await WebSocket.connect(client.liveQueryURL); 33 | var webSocket = await WebSocket.connect(client.liveQueryURL); 34 | channel = await new IOWebSocketChannel(webSocket); 35 | channel.sink.add(JSON.encode(connectMessage)); 36 | subscribeMessage['query']['className'] = className; 37 | channel.sink.add(JSON.encode(subscribeMessage)); 38 | channel.stream.listen((message) { 39 | Map actionData = JSON.decode(message); 40 | if (eventCallbacks.containsKey(actionData['op'])) 41 | eventCallbacks[actionData['op']](actionData); 42 | }); 43 | } 44 | 45 | void on(String op, Function callback){ 46 | eventCallbacks[op] = callback; 47 | } 48 | 49 | void close(){ 50 | channel.close(); 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /lib/src/parse_object.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:async'; 3 | 4 | import 'parse_base.dart'; 5 | import 'parse_http_client.dart'; 6 | 7 | 8 | 9 | class ParseObject implements ParseBaseObject { 10 | final String className; 11 | final ParseHTTPClient client; 12 | String path; 13 | Map objectData = {}; 14 | 15 | String get objectId => objectData['objectId']; 16 | ParseObject(String this.className, [ParseHTTPClient this.client]) { 17 | path = "/parse/classes/${className}"; 18 | } 19 | 20 | Future create([Map objectInitialData]) async { 21 | objectData = {}..addAll(objectData)..addAll(objectInitialData); 22 | 23 | final response = this.client.post("${client.baseURL}${path}", body: JSON.encode(objectData)); 24 | return response.then((value){ 25 | objectData = JSON.decode(value.body); 26 | return objectData; 27 | }); 28 | } 29 | 30 | Future get(attribute) async { 31 | final response = this.client.get(client.baseURL + "${path}/${objectId}"); 32 | return response.then((value){ 33 | objectData = JSON.decode(value.body); 34 | return objectData[attribute]; 35 | }); 36 | } 37 | 38 | void set(String attribute, dynamic value){ 39 | objectData[attribute] = value; 40 | } 41 | 42 | Future save([Map objectInitialData]){ 43 | objectData = {}..addAll(objectData)..addAll(objectInitialData); 44 | if (objectId == null){ 45 | return create(objectData); 46 | } 47 | else { 48 | final response = this.client.put( 49 | client.baseURL + "${path}/${objectId}", body: JSON.encode(objectData)); 50 | return response.then((value) { 51 | objectData = JSON.decode(value.body); 52 | return objectData; 53 | }); 54 | } 55 | } 56 | 57 | Future destroy(){ 58 | final response = this.client.delete(client.baseURL + "${path}/${objectId}"); 59 | return response.then((value){ 60 | return JSON.decode(value.body); 61 | }); 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /lib/src/parse_query.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'parse_base.dart'; 4 | import 'parse_http_client.dart'; 5 | 6 | class Query implements ParseBaseObject { 7 | String className; 8 | final ParseHTTPClient client; 9 | String path; 10 | Map results; 11 | Map constraint; 12 | 13 | String get objectId => null; 14 | Map objectData = {}; 15 | Query(String className, ParseHTTPClient client) 16 | : client = client; 17 | 18 | void equalTo (String key, dynamic value ) { 19 | 20 | } 21 | 22 | void notEqualTo(String key, dynamic value) { 23 | 24 | } 25 | 26 | void limit(int limit) { 27 | 28 | } 29 | 30 | void skip(int limit) { 31 | 32 | } 33 | 34 | void ascending(String attribute) { 35 | 36 | } 37 | 38 | void descending(String attribute) { 39 | 40 | } 41 | 42 | void startsWith(String key, dynamic value) { 43 | 44 | } 45 | 46 | Future first() { 47 | Map t = {}; 48 | foo() => t; 49 | return new Future(foo); 50 | } 51 | } -------------------------------------------------------------------------------- /lib/src/parse_user.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:async'; 3 | 4 | import 'parse_base.dart'; 5 | import 'parse_http_client.dart'; 6 | 7 | class User implements ParseBaseObject { 8 | final String className = '_User'; 9 | final ParseHTTPClient client; 10 | String password; 11 | String path; 12 | Map objectData = {}; 13 | 14 | String get objectId => objectData['objectId']; 15 | String get sessionId => objectData['sessionToken']; 16 | String get username => objectData['username']; 17 | String get userId => objectData['objectId']; 18 | 19 | 20 | User([ParseHTTPClient client]) 21 | : path = "/parse/classes/_User", 22 | client = client; 23 | 24 | void set(String attribute, dynamic value){ 25 | objectData[attribute] = value; 26 | } 27 | 28 | Future get(attribute) async { 29 | final response = this.client.get(client.baseURL + "${path}/${objectId}"); 30 | return response.then((value){ 31 | objectData = JSON.decode(value.body); 32 | return objectData[attribute]; 33 | }); 34 | } 35 | 36 | Future me(attribute) async { 37 | final response = this.client.get( 38 | client.baseURL + "${path}/me", 39 | headers: { 40 | "X-Parse-Session-Token": sessionId 41 | } 42 | ); 43 | return response.then((value){ 44 | objectData = JSON.decode(value.body); 45 | return objectData[attribute]; 46 | }); 47 | } 48 | 49 | Map _handleResponse(String response){ 50 | Map responseData = JSON.decode(response); 51 | if (responseData.containsKey('objectId')) { 52 | objectData = responseData; 53 | this.client.credentials.sessionId = sessionId; 54 | } 55 | return responseData; 56 | } 57 | 58 | 59 | void _resetObjectId(){ 60 | if (objectId != null) 61 | objectData.remove('objectId'); 62 | if (sessionId != null) 63 | objectData.remove('sessionToken'); 64 | } 65 | 66 | Future> signUp([Map objectInitialData]) async { 67 | if(objectInitialData != null) { 68 | objectData = {}..addAll(objectData)..addAll(objectInitialData); 69 | } 70 | _resetObjectId(); 71 | print(objectData); 72 | final response = this.client.post("${client.baseURL}${path}", 73 | headers: { 74 | 'X-Parse-Revocable-Session': "1", 75 | }, 76 | body: JSON.encode(objectData)); 77 | return response.then((value){ 78 | _handleResponse(value.body); 79 | return objectData; 80 | }); 81 | } 82 | 83 | Future> login() async { 84 | Uri url = new Uri( 85 | path: "${client.baseURL}/parse/login", 86 | queryParameters: { 87 | "username": objectData['username'], 88 | "password": objectData['password'] 89 | }); 90 | 91 | final response = this.client.post(url, 92 | headers: { 93 | 'X-Parse-Revocable-Session': "1", 94 | }); 95 | return response.then((value){ 96 | _handleResponse(value.body); 97 | return objectData; 98 | }); 99 | } 100 | 101 | Future> verificationEmailRequest() async { 102 | final response = this.client.post( 103 | "${client.baseURL}/parse/verificationEmailRequest", 104 | body: JSON.encode({"email": objectData['email']}) 105 | ); 106 | return response.then((value){ 107 | return _handleResponse(value.body); 108 | }); 109 | } 110 | 111 | Future> requestPasswordReset() async { 112 | final response = this.client.post( 113 | "${client.baseURL}/parse/requestPasswordReset", 114 | body: JSON.encode({"email": objectData['email']}) 115 | ); 116 | return response.then((value){ 117 | return _handleResponse(value.body); 118 | }); 119 | } 120 | 121 | Future> save([Map objectInitialData]){ 122 | objectData = {}..addAll(objectData)..addAll(objectInitialData); 123 | if (objectId == null){ 124 | return signUp(objectData); 125 | } 126 | else { 127 | final response = this.client.put( 128 | client.baseURL + "${path}/${objectId}", body: JSON.encode(objectData)); 129 | return response.then((value) { 130 | return _handleResponse(value.body); 131 | }); 132 | } 133 | } 134 | 135 | Future destroy(){ 136 | final response = this.client.delete( 137 | client.baseURL + "${path}/${objectId}", 138 | headers: { 139 | "X-Parse-Session-Token": sessionId 140 | } 141 | ); 142 | return response.then((value){ 143 | _handleResponse(value.body); 144 | return objectId; 145 | }); 146 | } 147 | 148 | Future> all(){ 149 | final response = this.client.get( 150 | client.baseURL + "${path}" 151 | ); 152 | return response.then((value) { 153 | return _handleResponse(value.body); 154 | }); 155 | } 156 | } 157 | 158 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: parse_server 2 | description: Parse Server Dart SDK. 3 | version: 0.0.4 4 | homepage: https://www.github.com/lotux 5 | author: lotux 6 | 7 | environment: 8 | sdk: '>=1.20.1 <2.0.0' 9 | 10 | dependencies: 11 | path: ^1.4.1 12 | web_socket_channel: ^1.0.6 13 | http: ^0.11.3 14 | 15 | dev_dependencies: 16 | test: ^0.12.0 17 | -------------------------------------------------------------------------------- /test/parse_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:parse_server/parse_server.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | group('A group of tests', () { 6 | Parse parse; 7 | 8 | setUp(() { 9 | parse = new Parse(new Credentials("appId"), "http://localhost"); 10 | }); 11 | 12 | test('First Test', () { 13 | expect(parse.credentials, isTrue); 14 | }); 15 | }); 16 | } 17 | --------------------------------------------------------------------------------