diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml index fdd01077d..97a3d9f67 100644 --- a/.idea/libraries/Dart_Packages.xml +++ b/.idea/libraries/Dart_Packages.xml @@ -1,25 +1,26 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/.idea/libraries/Dart_SDK.xml b/.idea/libraries/Dart_SDK.xml index b257e38dc..128e0bcc2 100644 --- a/.idea/libraries/Dart_SDK.xml +++ b/.idea/libraries/Dart_SDK.xml @@ -1,25 +1,25 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/.idea/libraries/Flutter_Plugins.xml b/.idea/libraries/Flutter_Plugins.xml index b7751cdc9..22987bdb6 100644 --- a/.idea/libraries/Flutter_Plugins.xml +++ b/.idea/libraries/Flutter_Plugins.xml @@ -1,7 +1,7 @@ - + diff --git a/.idea/workspace.xml b/.idea/workspace.xml index bbb29f651..54e4d0a5c 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -11,17 +11,12 @@ - - - - - - - + - + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + + - - - - - - - - - @@ -183,6 +149,12 @@ ParseObject userLogger user + toJson + toString + encode( + Json() + saV + save part @@ -217,7 +189,6 @@ - @@ -264,8 +237,6 @@ - - @@ -273,57 +244,32 @@ + + + + - + @@ -331,7 +277,7 @@ - + @@ -356,6 +302,40 @@ + + + + + + + @@ -517,77 +478,99 @@ - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + - - - - - - - + - - - - + - + + + + + + + + + + - + + + + + + + + + + + + + + + - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -612,55 +595,36 @@ - file://$PROJECT_DIR$/lib/src/objects/parse_user.dart - 221 - - file://$PROJECT_DIR$/lib/src/objects/parse_user.dart - 277 - - - file://$PROJECT_DIR$/example/lib/main.dart - 131 - - - file://$PROJECT_DIR$/example/lib/main.dart - 130 - - - file://$PROJECT_DIR$/lib/src/objects/parse_user.dart - 207 - - - file://$PROJECT_DIR$/lib/src/objects/parse_user.dart - 205 - - - file://$PROJECT_DIR$/lib/src/objects/parse_response.dart - 43 - file://$PROJECT_DIR$/lib/src/objects/parse_user.dart - 267 - - - file://$PROJECT_DIR$/lib/src/objects/parse_response.dart - 45 - + + + + + + + + getObjectData() + Dart + EXPRESSION + await ParseCoreData().getStore().getString(key) Dart @@ -709,273 +673,288 @@ - - - - - - - - - - - - - + + + + + + + + + + + + - - + + - + + + - - + + - - - - + - - + + - + - - + + - - - - - - + - - + + - + - - + + - - + - - + + - + - - + + + + + - - - - + + + + + + + - - - + - - - - + + + + + - + - - + + - + + + + + + + + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + + - - + + - + - - + + - + - - + + - + - - - - - + + - + - - + + - + - - - - - + + - + - - + + - + - - + + - + - - + + + + + + + + + - - + + - + - - - - - + + - + - - + + - + - - + + - + - - + + - + - - + + + + + + + + + + + + + diff --git a/CHANGELOG.md b/CHANGELOG.md index a61be15ec..2fcc4ea5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.5 + +Corrected save. Now formatted items correctly for saving on server + ## 1.0.4 Bug fix for get all items diff --git a/README.md b/README.md index 054f7134e..298331779 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 ``` dependencies: - parse_server_sdk: ^1.0.3 + parse_server_sdk: ^1.0.5 ``` 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. @@ -22,17 +22,17 @@ Once you have the library added to your project, upon first call to your app (Si ``` Parse().initialize( - ApplicationConstants.PARSE_APPLICATION_ID, - ApplicationConstants.PARSE_SERVER_URL); + ApplicationConstants.keyApplicationId, + ApplicationConstants.keyParseServerUrl); ``` It's possible to add other params, such as ... ``` Parse().initialize( - ApplicationConstants.PARSE_APPLICATION_ID, - ApplicationConstants.PARSE_SERVER_URL, - masterKey: ApplicationConstants.PARSE_MASTER_KEY, + ApplicationConstants.keyApplicationId, + ApplicationConstants.keyParseServerUrl, + masterKey: ApplicationConstants.keyParseMasterKey, debug: true, liveQuery: true); ``` @@ -54,9 +54,9 @@ Or you can get an object by its objectId: var dietPlan = await DietPlan().get('R5EonpUDWy'); if (dietPlan.success) { - print(ApplicationConstants.APP_NAME + ": " + (dietPlan.result as DietPlan).toString()); + print(ApplicationConstants.keyAppName + ": " + (dietPlan.result as DietPlan).toString()); } else { - print(ApplicationConstants.APP_NAME + ": " + dietPlan.exception.message); + print(ApplicationConstants.keyAppName + ": " + dietPlan.exception.message); } ``` @@ -66,17 +66,17 @@ You can create complex queries to really put your database to the test: ``` var queryBuilder = QueryBuilder(DietPlan()) - ..startsWith(DietPlan.NAME, "Keto") - ..greaterThan(DietPlan.FAT, 64) - ..lessThan(DietPlan.FAT, 66) - ..equals(DietPlan.CARBS, 5); + ..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.APP_NAME + ": " + ((response.result as List).first as DietPlan).toString()); + print(ApplicationConstants.keyAppName + ": " + ((response.result as List).first as DietPlan).toString()); } else { - print(ApplicationConstants.APP_NAME + ": " + response.exception.message); + print(ApplicationConstants.keyAppName + ": " + response.exception.message); } ``` @@ -124,18 +124,18 @@ You can create your own ParseObjects or convert your existing objects into Parse ``` class DietPlan extends ParseObject implements ParseCloneable { - DietPlan() : super(DIET_PLAN); + 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 DIET_PLAN = 'Diet_Plans'; - static const String NAME = 'Name'; + static const String _keyTableName = 'Diet_Plans'; + static const String keyName = 'Name'; - String get name => get(NAME); - set name(String name) => set(NAME, name); + String get name => get(keyName); + set name(String name) => set(keyName, name); } ``` diff --git a/example/lib/application_constants.dart b/example/lib/application_constants.dart index e2b7f38b0..9e77a8bc0 100644 --- a/example/lib/application_constants.dart +++ b/example/lib/application_constants.dart @@ -1,6 +1,6 @@ -abstract class ApplicationConstants { // Start - static const String APP_NAME = ""; - static const String PARSE_APPLICATION_ID = ""; - static const String PARSE_MASTER_KEY = ""; - static const String PARSE_SERVER_URL = ""; -} // End \ No newline at end of file +abstract class ApplicationConstants { + static const String keyAppName = ""; + static const String keyParseApplicationId = ""; + static const String keyParseMasterKey = ""; + static const String keyParseServerUrl = ""; +} \ No newline at end of file diff --git a/example/lib/diet_plan.dart b/example/lib/diet_plan.dart index f8ad5e053..52995379d 100644 --- a/example/lib/diet_plan.dart +++ b/example/lib/diet_plan.dart @@ -1,45 +1,39 @@ -import 'dart:convert'; import 'dart:core'; import 'package:parse_server_sdk/parse.dart'; class DietPlan extends ParseObject implements ParseCloneable { - DietPlan() : super(DIET_PLAN); + 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 DIET_PLAN = 'Diet_Plans'; - static const String NAME = 'Name'; - static const String DESCRIPTION = 'Description'; - static const String PROTEIN = 'Protein'; - static const String CARBS = 'Carbs'; - static const String FAT = 'Fat'; - static const String STATUS = 'Status'; + static const String _keyTableName = 'Diet_Plans'; + static const String keyName = 'Name'; + static const String keyDescription = 'Description'; + static const String keyProtein = 'Protein'; + static const String keyCarbs = 'Carbs'; + static const String keyFat = 'Fat'; + static const String keyStatus = 'Status'; - String get name => get(NAME); - set name(String name) => set(NAME, name); + String get name => get(keyName); + set name(String name) => set(keyName, name); - String get description => get(DESCRIPTION); - set description(String description) => set(DESCRIPTION, name); + String get description => get(keyDescription); + set description(String description) => set(keyDescription, name); - int get protein => get(PROTEIN); - set protein(int protein) => super.set(PROTEIN, protein); + int get protein => get(keyProtein); + set protein(int protein) => super.set(keyProtein, protein); - int get carbs => get(CARBS); - set carbs(int carbs) => set(CARBS, carbs); + int get carbs => get(keyCarbs); + set carbs(int carbs) => set(keyCarbs, carbs); - int get fat => get(FAT); - set fat(int fat) => set(FAT, fat); + int get fat => get(keyFat); + set fat(int fat) => set(keyFat, fat); - int get status => get(STATUS); - set status(int status) => set(STATUS, status); - - @override - String toString() { - return toJson().toString(); - } + int get status => get(keyStatus); + set status(int status) => set(keyStatus, status); } diff --git a/example/lib/main.dart b/example/lib/main.dart index c896974ec..ebd6e3d0e 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -15,7 +15,6 @@ class _MyAppState extends State { void initState() { super.initState(); initParse(); - runTestQueries(); } @override @@ -36,14 +35,23 @@ class _MyAppState extends State { initParse() async { // Initialize parse - Parse().initialize(ApplicationConstants.PARSE_APPLICATION_ID, - ApplicationConstants.PARSE_SERVER_URL, - masterKey: ApplicationConstants.PARSE_MASTER_KEY, - appName: ApplicationConstants.APP_NAME, + Parse().initialize(ApplicationConstants.keyParseApplicationId, + ApplicationConstants.keyParseServerUrl, + masterKey: ApplicationConstants.keyParseMasterKey, + appName: ApplicationConstants.keyAppName, debug: true); + + // Check server is healthy and live - Debug is on in this instance so check logs for result + var response = await Parse().healthCheck(); + if (response.success){ + runTestQueries(); + } else { + print("Server health check failed"); + } } runTestQueries() { + createItem(); getAllItems(); getAllItemsByName(); getSingleItem(); @@ -52,33 +60,46 @@ class _MyAppState extends State { initUser(); } + void createItem() async { + + var newObject = ParseObject('TestObjectForApi'); + newObject.set('name', 'testItem'); + newObject.set('age', 26); + + var apiResponse = await newObject.create(); + + if (apiResponse.success && apiResponse.result != null) { + print(ApplicationConstants.keyAppName + ": " + apiResponse.result.toString()); + } + } + void getAllItemsByName() async { var apiResponse = await ParseObject('ParseTableName').getAll(); - if (apiResponse.success) { + if (apiResponse.success && apiResponse.result != null) { for (var testObject in apiResponse.result) { - print(ApplicationConstants.APP_NAME + ": " + testObject.toString()); + print(ApplicationConstants.keyAppName + ": " + testObject.toString()); } } } void getAllItems() async { - var response = await DietPlan().getAll(); + var apiResponse = await DietPlan().getAll(); - if (response.success) { - for (var plan in response.result) { - print(ApplicationConstants.APP_NAME + ": " + (plan as DietPlan).name); + if (apiResponse.success && apiResponse.result != null) { + for (var plan in apiResponse.result) { + print(ApplicationConstants.keyAppName + ": " + (plan as DietPlan).name); } } else { - print(ApplicationConstants.APP_NAME + ": " + response.error.message); + print(ApplicationConstants.keyAppName + ": " + apiResponse.error.message); } } void getSingleItem() async { - var response = await DietPlan().getObject('R5EonpUDWy'); + var apiResponse = await DietPlan().getObject('R5EonpUDWy'); - if (response.success) { - var dietPlan = (response.result as DietPlan); + if (apiResponse.success && apiResponse.result != null) { + var dietPlan = (apiResponse.result as DietPlan); // Shows example of storing values in their proper type and retrieving them dietPlan.set('RandomInt', 8); @@ -95,21 +116,21 @@ class _MyAppState extends State { if (newDietPlanFromPin != null) print('Retreiving from pin worked!'); } else { - print(ApplicationConstants.APP_NAME + ": " + response.error.message); + print(ApplicationConstants.keyAppName + ": " + apiResponse.error.message); } } void query() async { var queryBuilder = QueryBuilder(DietPlan()) - ..greaterThan(DietPlan.FAT, 20) - ..descending(DietPlan.FAT); + ..greaterThan(DietPlan.keyFat, 20) + ..descending(DietPlan.keyFat); - var response = await queryBuilder.query(); + var apiResponse = await queryBuilder.query(); - if (response.success) { - print("Result: ${((response.result as List).first as DietPlan).toString()}"); + if (apiResponse.success && apiResponse.result != null) { + print("Result: ${((apiResponse.result as List).first as DietPlan).toString()}"); } else { - print("Result: ${response.error.message}"); + print("Result: ${apiResponse.error.message}"); } } @@ -132,7 +153,7 @@ class _MyAppState extends State { if (destroyResponse.success) print('object has been destroyed!'); // Returns type ParseResponse as its a query, not a single result - var response = await ParseUser.all(); + await ParseUser.all(); } function() { diff --git a/lib/parse.dart b/lib/parse.dart index 0a12ff8fc..822840149 100644 --- a/lib/parse.dart +++ b/lib/parse.dart @@ -4,35 +4,51 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:http/http.dart'; import 'package:meta/meta.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:web_socket_channel/io.dart'; part 'src/base/parse_constants.dart'; + part 'src/data/parse_core_data.dart'; + part 'src/enums/parse_enum_api_rq.dart'; + part 'src/network/parse_http_client.dart'; + part 'src/network/parse_livequery.dart'; + part 'src/network/parse_query.dart'; + part 'src/objects/parse_base.dart'; + +part 'src/objects/parse_clonable.dart'; + part 'src/objects/parse_error.dart'; + part 'src/objects/parse_function.dart'; + part 'src/objects/parse_geo_point.dart'; + part 'src/objects/parse_object.dart'; + part 'src/objects/parse_response.dart'; + part 'src/objects/parse_user.dart'; -part 'src/objects/parse_clonable.dart'; -part 'src/utils/parse_utils_date.dart'; -part 'src/utils/parse_utils_objects.dart'; -part 'src/utils/parse_utils.dart'; -part 'src/utils/parse_encoder.dart'; + part 'src/utils/parse_decoder.dart'; + +part 'src/utils/parse_encoder.dart'; + part 'src/utils/parse_logger.dart'; +part 'src/utils/parse_utils.dart'; + class Parse { ParseCoreData data; final ParseHTTPClient client = new ParseHTTPClient(); + bool _hasBeenInitialised = false; /// To initialise Parse Server in your application /// @@ -46,8 +62,12 @@ class Parse { // debug: true, // liveQuery: true); // ``` - Parse initialize(appId, serverUrl, - {debug, appName, liveQueryUrl, masterKey, sessionId}) { + Parse initialize(String appId, String serverUrl, + {bool debug, + String appName, + String liveQueryUrl, + String masterKey, + String sessionId}) { ParseCoreData.init(appId, serverUrl, debug: debug, appName: appName, @@ -55,15 +75,30 @@ class Parse { masterKey: masterKey, sessionId: sessionId); - return _newInstance(ParseCoreData()); + ParseCoreData().initStorage(); + + _hasBeenInitialised = true; + + return Parse(); } - /// Creates a singleton instance of [ParseCoreData] that contains all the server information - Parse _newInstance(ParseCoreData data) { - var parse = Parse(); - parse.data = data; - parse.client.data = data; - data.initStorage(); - return parse; + bool hasParseBeenInitialised() => _hasBeenInitialised; + + Future healthCheck() async { + + ParseResponse parseResponse; + + try { + var response = await ParseHTTPClient().get("${ParseCoreData().serverUrl}$keyEndPointHealth"); + parseResponse = ParseResponse.handleResponse(this, response); + } on Exception catch (e) { + parseResponse = ParseResponse.handleException(e); + } + + if (ParseCoreData().debug) { + logger(ParseCoreData().appName, keyClassMain, ParseApiRQ.healthCheck.toString(), parseResponse); + } + + return parseResponse; } } diff --git a/lib/src/base/parse_constants.dart b/lib/src/base/parse_constants.dart index c6ea7893d..89002b7dd 100644 --- a/lib/src/base/parse_constants.dart +++ b/lib/src/base/parse_constants.dart @@ -1,17 +1,43 @@ part of flutter_parse_sdk; -// Utils -const String PARSE_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm"; +// Library +const String keySdkVersion = '1.0.5'; +const String keyLibraryName= 'Flutter Parse SDK'; + +// End Points +const String keyEndPointUserName = '/users/me'; +const String keyEndPointLogin = '/login'; +const String keyEndPointVerificationEmail = '/verificationEmailRequest'; +const String keyEndPointRequestPasswordReset = '/requestPasswordReset'; +const String keyEndPointClasses = '/classes/'; +const String keyEndPointHealth = '/health'; // ParseObject variables -const String OBJECT_ID = 'objectId'; -const String CREATED_AT = 'createdAt'; -const String UPDATED_AT = 'updatedAT'; +const String keyVarClassName = 'className'; +const String keyVarObjectId = 'objectId'; +const String keyVarCreatedAt = 'createdAt'; +const String keyVarUpdatedAt = 'updatedAt'; +const String keyVarUsername = 'username'; +const String keyVarEmail = 'email'; +const String keyVarPassword = 'password'; +const String keyVarAcl = 'ACL'; + +// Classes +const String keyClassMain = 'ParseMain'; +const String keyClassUser = '_User'; // Headers -const String HEADER_SESSION_TOKEN = 'X-Parse-Session-Token'; -const String HEADER_REVOCABLE_SESSION = 'X-Parse-Revocable-Session'; +const String keyHeaderSessionToken = 'X-Parse-Session-Token'; +const String keyHeaderRevocableSession = 'X-Parse-Revocable-Session'; +const String keyHeaderUserAgent = 'user-agent'; +const String keyHeaderApplicationId = 'X-Parse-Application-Id'; +const String keyHeaderContentType = 'Content-Type'; +const String keyHeaderContentTypeJson = 'application/json'; +const String keyHeaderMasterKey = 'X-Parse-Master-Key'; + +// URL params +const String keyParamSessionToken = 'sessionToken'; // Storage -const String PARSE_STORE_BASE = 'flutter_parse_sdk_'; -const String PARSE_STORE_USER = "${PARSE_STORE_BASE}user"; \ No newline at end of file +const String keyParseStoreBase = 'flutter_parse_sdk_'; +const String keyParseStoreUser = "${keyParseStoreBase}user"; \ No newline at end of file diff --git a/lib/src/enums/parse_enum_api_rq.dart b/lib/src/enums/parse_enum_api_rq.dart index 51f04bfdf..45ec4339d 100644 --- a/lib/src/enums/parse_enum_api_rq.dart +++ b/lib/src/enums/parse_enum_api_rq.dart @@ -2,6 +2,7 @@ part of flutter_parse_sdk; /// Used to define the API calls made in ParseObject logs enum ParseApiRQ { + healthCheck, get, getAll, create, diff --git a/lib/src/network/parse_http_client.dart b/lib/src/network/parse_http_client.dart index 9fa78c574..8b549938c 100644 --- a/lib/src/network/parse_http_client.dart +++ b/lib/src/network/parse_http_client.dart @@ -3,7 +3,7 @@ part of flutter_parse_sdk; /// Creates a custom version of HTTP Client that has Parse Data Preset class ParseHTTPClient extends BaseClient { final Client _client = Client(); - final String _userAgent = "Flutter Parse SDK 1.0.4"; + final String _userAgent = "$keyLibraryName $keySdkVersion"; ParseCoreData data = ParseCoreData(); ParseHTTPClient(); @@ -11,10 +11,10 @@ class ParseHTTPClient extends BaseClient { /// Overrides the call method for HTTP Client and adds custom headers @override Future send(BaseRequest request) { - request.headers['user-agent'] = _userAgent; - request.headers['X-Parse-Application-Id'] = data.applicationId; - request.headers['Content-Type'] = 'application/json'; - if (data.masterKey != null) request.headers['X-Parse-Master-Key'] = data.masterKey; + request.headers[keyHeaderUserAgent] = _userAgent; + request.headers[keyHeaderApplicationId] = data.applicationId; + //request.headers[keyHeaderContentType] = keyHeaderContentTypeJson; + if (data.masterKey != null) request.headers[keyHeaderMasterKey] = data.masterKey; return _client.send(request); } } diff --git a/lib/src/network/parse_query.dart b/lib/src/network/parse_query.dart index 5dc3678cc..9a53efd33 100644 --- a/lib/src/network/parse_query.dart +++ b/lib/src/network/parse_query.dart @@ -149,7 +149,7 @@ class QueryBuilder { /// Finishes the query and calls the server /// /// Make sure to call this after defining your queries - query() async { + Future query() async { return object.query(_buildQuery()); } @@ -178,7 +178,7 @@ class QueryBuilder { /// Creates a query param using the column, the value and the queryOperator /// that the column and value are being queried against - _buildQueryWithColumnValueAndOperator(MapEntry columnAndValue, String queryOperator) { + MapEntry _buildQueryWithColumnValueAndOperator(MapEntry columnAndValue, String queryOperator) { var key = columnAndValue.key; var value = convertValueToCorrectType(columnAndValue.value); @@ -200,7 +200,7 @@ class QueryBuilder { /// This joins queries that should be joined together... e.g. age > 10 && /// age < 20, this would be similar to age > 10 < 20 - _checkForMultipleColumnInstances(List queries) { + List _checkForMultipleColumnInstances(List queries) { List sanitisedQueries = List(); List keysAlreadyCompacted = List(); @@ -247,7 +247,7 @@ class QueryBuilder { } /// Adds the limiters to the query, i.e. skip=10, limit=10 - getLimiters(Map map) { + String getLimiters(Map map) { String result = ""; map.forEach((key, value) { result = (result != null) ? result + "&$key=$value" : "&$key=$value"; diff --git a/lib/src/objects/parse_base.dart b/lib/src/objects/parse_base.dart index d9bd44a86..20e3f1798 100644 --- a/lib/src/objects/parse_base.dart +++ b/lib/src/objects/parse_base.dart @@ -1,62 +1,105 @@ part of flutter_parse_sdk; abstract class ParseBase { - String className; setClassName(String className) => this.className = className; + String getClassName() => className; /// Stores all the values of a class Map _objectData = Map(); /// Returns [String] objectId - String get objectId => get(OBJECT_ID); - set objectId(String objectId) => set(OBJECT_ID, objectId); + String get objectId => get(keyVarObjectId); + + set objectId(String objectId) => set(keyVarObjectId, objectId); /// Returns [DateTime] createdAt - DateTime get createdAt => stringToDateTime(get(CREATED_AT)); - set createdAt(DateTime createdAt) => set(CREATED_AT, dateTimeToString(createdAt)); + DateTime get createdAt => get(keyVarCreatedAt); /// Returns [DateTime] updatedAt - DateTime get updatedAt => stringToDateTime(get(UPDATED_AT)); - set updatedAt(DateTime updatedAt) => set(UPDATED_AT, dateTimeToString(updatedAt)); + DateTime get updatedAt => get(keyVarUpdatedAt); /// Converts object to [String] in JSON format - @protected String toJson() { - return JsonEncoder().convert(getObjectData()); + @protected + toJson({bool forApiRQ: false}) { + final map = { + keyVarClassName: className, + }; + + if (objectId != null) { + map[keyVarObjectId] = objectId; + } + + if (createdAt != null) { + map[keyVarCreatedAt] = createdAt.toIso8601String(); + } + + if (updatedAt != null) { + map[keyVarUpdatedAt] = updatedAt.toIso8601String(); + } + + getObjectData().forEach((key, value) { + if (!map.containsKey(key)) map[key] = parseEncode(value); + }); + + if (forApiRQ) { + map.remove(keyVarCreatedAt); + map.remove(keyVarUpdatedAt); + map.remove(keyVarClassName); + map.remove(keyVarAcl); + map.remove(keyParamSessionToken); + } + + return map; + } + + @override + String toString() => json.encode(toJson()); + + @protected + fromJson(Map objectData) { + objectData.forEach((key, value) { + if (key == className || key == '__type') { + // NO OP + } else if (key == keyVarObjectId) { + objectId = value; + } else if (key == keyVarCreatedAt) { + set(keyVarCreatedAt, DateTime.parse(value)); + } else if (key == keyVarUpdatedAt) { + set(keyVarUpdatedAt, DateTime.parse(value)); + } else { + getObjectData()[key] = parseDecode(value); + } + }); + + return this; } /// Creates a copy of this class - @protected copy() => fromJson(JsonDecoder().convert(toJson())); + @protected + copy() => fromJson(json.decode(toJson())); /// Sets all the objects variables - @protected setObjectData(Map objectData) { - _objectData = objectData; - } + @protected + void setObjectData(Map objectData) => _objectData = objectData; /// Returns the objects variables - @protected getObjectData() { - return _objectData; - } + @protected + Map getObjectData() => _objectData; /// Saves in storage - @protected saveInStorage(String key) async { - await ParseCoreData().getStore().setString(key, toJson()); - } - - @protected fromJson(Map objectData) { - if (getObjectData() == null) setObjectData(Map()); - getObjectData().addAll(objectData); - return this; - } + @protected + void saveInStorage(String key) async => + await ParseCoreData().getStore().setString(key, toString()); /// Sets type [T] from objectData /// /// To set an int, call setType and an int will be saved /// [bool] forceUpdate is always true, if unsure as to whether an item is /// needed or not, set to false - set(String key, T value, {bool forceUpdate: true}) { + void set(String key, T value, {bool forceUpdate: true}) { if (value != null) { if (getObjectData().containsKey(key)) { if (forceUpdate) getObjectData()[key] = value; @@ -86,10 +129,11 @@ abstract class ParseBase { /// Saves item to simple key pair value storage /// /// Replicates Android SDK pin process and saves object to storage - pin() async { + Future pin() async { if (objectId != null) { - var itemToSave = toJson(); - await ParseCoreData().getStore().setString(objectId, itemToSave); + await ParseCoreData() + .getStore() + .setString(objectId, json.encode(toJson())); return true; } else { return false; @@ -99,14 +143,14 @@ abstract class ParseBase { /// Saves item to simple key pair value storage /// /// Replicates Android SDK pin process and saves object to storage - unpin() async { + Future unpin() async { if (objectId != null) { - var itemToSave = await ParseCoreData().getStore().setString( - objectId, null); + var itemToSave = + await ParseCoreData().getStore().setString(objectId, null); if (itemToSave) return true; - } else { - return false; } + + return false; } /// Saves item to simple key pair value storage @@ -117,10 +161,11 @@ abstract class ParseBase { var itemFromStore = ParseCoreData().getStore().getString(objectId); if (itemFromStore != null) { - Map itemFromStoreMap = JsonDecoder().convert( - itemFromStore); - fromJson(itemFromStoreMap); - return this; + var map = json.decode(itemFromStore); + + if (map != null) { + return fromJson(map); + } } } return null; diff --git a/lib/src/objects/parse_error.dart b/lib/src/objects/parse_error.dart index 7cce784f8..39cdc7e34 100644 --- a/lib/src/objects/parse_error.dart +++ b/lib/src/objects/parse_error.dart @@ -8,6 +8,7 @@ class ParseError { // SDK errors / Errors 1: 'No Results', + 2: 'OK', 400: 'Bad Request', // Parse specific / Exceptions diff --git a/lib/src/objects/parse_object.dart b/lib/src/objects/parse_object.dart index 1a2f6aa2f..66b75736f 100644 --- a/lib/src/objects/parse_object.dart +++ b/lib/src/objects/parse_object.dart @@ -1,8 +1,7 @@ part of flutter_parse_sdk; class ParseObject extends ParseBase implements ParseCloneable { - - ParseObject.clone(String className): this('className'); + ParseObject.clone(String className) : this('className'); @override clone(Map map) => ParseObject.clone(className)..fromJson(map); @@ -16,23 +15,23 @@ class ParseObject extends ParseBase implements ParseCloneable { /// [String] className refers to the Table Name in your Parse Server, /// [bool] debug will overwrite the current default debug settings and /// [ParseHttpClient] can be overwritten to create your own HTTP Client - ParseObject(String className, {bool debug: false}): super() { + ParseObject(String className, {bool debug: false}) : super() { setClassName(className); - _path = "/classes/$className"; + _path = "$keyEndPointClasses$className"; setClient(ParseHTTPClient()); - setDebug(isDebugEnabled(_client, objectLevelDebug: debug)); + setDebug(isDebugEnabled(objectLevelDebug: debug)); } - setDebug(bool debug){ + void setDebug(bool debug) { _debug = debug; } - setClient(ParseHTTPClient client){ + void setClient(ParseHTTPClient client) { _client = client; } /// Gets an object from the server using it's [String] objectId - getObject(String objectId) async { + Future getObject(String objectId) async { try { var uri = "${ParseCoreData().serverUrl}$_path"; if (objectId != null) uri += "/$objectId"; @@ -44,7 +43,7 @@ class ParseObject extends ParseBase implements ParseCloneable { } /// Gets all objects from this table - Limited response at the moment - getAll() async { + Future getAll() async { try { var result = await _client.get("${ParseCoreData().serverUrl}$_path"); return handleResponse(result, ParseApiRQ.getAll); @@ -54,10 +53,11 @@ class ParseObject extends ParseBase implements ParseCloneable { } /// Creates a new object and saves it online - create() async { + Future create() async { try { var uri = _client.data.serverUrl + "$_path"; - var result = await _client.post(uri, body: JsonEncoder().convert(getObjectData())); + var body = json.encode(toJson(forApiRQ: true)); + var result = await _client.post(uri, body: body); return handleResponse(result, ParseApiRQ.create); } on Exception catch (e) { return handleException(e, ParseApiRQ.create); @@ -65,13 +65,14 @@ class ParseObject extends ParseBase implements ParseCloneable { } /// Saves the current object online - save() async { + Future save() async { if (getObjectData() == null) { return create(); } else { try { var uri = "${ParseCoreData().serverUrl}$_path/$objectId"; - var result = await _client.put(uri, body: JsonEncoder().convert(getObjectData())); + var body = json.encode(toJson(forApiRQ: true)); + var result = await _client.put(uri, body: body); return handleResponse(result, ParseApiRQ.save); } on Exception catch (e) { return handleException(e, ParseApiRQ.save); @@ -80,7 +81,7 @@ class ParseObject extends ParseBase implements ParseCloneable { } /// Can be used to create custom queries - query(String query) async { + Future query(String query) async { try { var uri = "${ParseCoreData().serverUrl}$_path?$query"; var result = await _client.get(uri); @@ -91,7 +92,7 @@ class ParseObject extends ParseBase implements ParseCloneable { } /// Deletes the current object locally and online - delete(String path, String objectId) async { + Future delete(String path, String objectId) async { try { var uri = "${ParseCoreData().serverUrl}$_path/$objectId"; var result = await _client.delete(uri); @@ -107,7 +108,8 @@ class ParseObject extends ParseBase implements ParseCloneable { ParseResponse parseResponse = ParseResponse.handleResponse(this, response); if (_debug) { - logger(ParseCoreData().appName, className, type.toString(), parseResponse); + logger( + ParseCoreData().appName, className, type.toString(), parseResponse); } return parseResponse; @@ -119,7 +121,8 @@ class ParseObject extends ParseBase implements ParseCloneable { ParseResponse parseResponse = ParseResponse.handleException(exception); if (_debug) { - logger(ParseCoreData().appName, className, type.toString(), parseResponse); + logger( + ParseCoreData().appName, className, type.toString(), parseResponse); } return parseResponse; diff --git a/lib/src/objects/parse_response.dart b/lib/src/objects/parse_response.dart index 7d46fe0e1..7c4cb22ec 100644 --- a/lib/src/objects/parse_response.dart +++ b/lib/src/objects/parse_response.dart @@ -8,20 +8,23 @@ class ParseResponse { /// Handles all the ParseObject responses /// - /// There are 3 probable outcomes from a Parse API call, + /// There are 4 probable outcomes from a Parse API call, /// 1. Fail - [ParseResponse()] will be returned with further details /// 2. Success but no results. [ParseResponse()] is returned. - /// 3. Success with results. Again [ParseResponse()] is returned - static handleResponse(ParseBase object, Response apiResponse) { + /// 3. Success with simple OK. + /// 4. Success with results. Again [ParseResponse()] is returned + static handleResponse(dynamic object, Response apiResponse) { var parseResponse = ParseResponse(); if (apiResponse != null) { parseResponse.statusCode = apiResponse.statusCode; - if (apiResponse.statusCode != 200) { + if (apiResponse.statusCode != 200 && apiResponse.statusCode != 201) { return _handleError(parseResponse, apiResponse); } else if (apiResponse.body == "{\"results\":[]}"){ - return _handleSuccessWithNoResults(parseResponse, "Successful request, but no results found"); + return _handleSuccessWithNoResults(parseResponse, 1, "Successful request, but no results found"); + } else if (apiResponse.body == "OK"){ + return _handleSuccessWithNoResults(parseResponse, 2, "Successful request"); } else { return _handleSuccess(parseResponse, object, apiResponse.body); } @@ -32,7 +35,7 @@ class ParseResponse { } /// Handles exception instead of throwing an exception - static handleException(Exception exception) { + static ParseResponse handleException(Exception exception) { var response = ParseResponse(); response.error = ParseError(message: exception.toString(), isTypeOfException: true); return response; @@ -47,14 +50,15 @@ class ParseResponse { } /// Handles successful responses with no results - static ParseResponse _handleSuccessWithNoResults(ParseResponse response, String value) { + static ParseResponse _handleSuccessWithNoResults(ParseResponse response, int code, String value) { + response.success = true; response.statusCode = 200; - response.error = ParseError(code: 1, message: value); + response.error = ParseError(code: code, message: value); return response; } /// Handles successful response with results - static ParseResponse _handleSuccess(ParseResponse response, ParseBase object, String responseBody) { + static ParseResponse _handleSuccess(ParseResponse response, dynamic object, String responseBody) { response.success = true; var map = JsonDecoder().convert(responseBody) as Map; @@ -69,20 +73,20 @@ class ParseResponse { } /// Handles a response with a multiple result object - static _handleMultipleResults(ParseBase object, dynamic map) { + static _handleMultipleResults(dynamic object, dynamic map) { var resultsList = List(); for (var value in map) { - var newObject = _handleSingleResult(object, value); - resultsList.add(newObject); + resultsList.add(_handleSingleResult(object, value)); } return resultsList; } /// Handles a response with a single result object - static _handleSingleResult(ParseBase object, map) { - if (object is ParseCloneable) return (object as ParseCloneable).clone(map); + static _handleSingleResult(dynamic object, map) { + if (object is Parse) return map; + if (object is ParseCloneable) return object.clone(map); return null; } } diff --git a/lib/src/objects/parse_user.dart b/lib/src/objects/parse_user.dart index 84c8d375e..7c4cb22ec 100644 --- a/lib/src/objects/parse_user.dart +++ b/lib/src/objects/parse_user.dart @@ -1,288 +1,92 @@ part of flutter_parse_sdk; -class ParseUser extends ParseBase implements ParseCloneable { +class ParseResponse { + bool success = false; + int statusCode = -1; + dynamic result; + ParseError error; - ParseUser.clone(Map map): this(map[USERNAME],map[PASSWORD],map[EMAIL]); - - @override - clone(Map map) => ParseUser.clone(map)..fromJson(map); - - static final String path = "/classes/_User"; - - bool _debug; - ParseHTTPClient _client; - - Map get acl => super.get(ACL); - set acl(Map acl) => set(ACL, acl); - - String get username => super.get(USERNAME); - set username(String username) => set(USERNAME, username); - - String get password => super.get(PASSWORD); - set password(String password) => set(PASSWORD, password); - - String get emailAddress => super.get(EMAIL); - set emailAddress(String emailAddress) => set(EMAIL, emailAddress); - - /// Creates an instance of ParseUser - /// - /// Users can set whether debug should be set on this class with a [bool], - /// they can also create thier own custom version of [ParseHttpClient] - /// - /// Creates a new user locally - /// - /// Requires [String] username, [String] password. [String] email address - /// is required as well to create a full new user object on ParseServer. Only - /// username and password is required to login - ParseUser(String username, String password, String emailAddress, {bool debug, ParseHTTPClient client}) : super() { - client == null ? _client = ParseHTTPClient() : _client = client; - _debug = isDebugEnabled(client, objectLevelDebug: debug); - - this.username = username; - this.password = password; - this.emailAddress = emailAddress; - - setClassName('_User'); - } - - /// Returns a [String] that's human readable. Ideal for printing logs - @override - String toString() => "User ($objectId): Username: $username, Email Address:$emailAddress"; - - static const String USERNAME = 'username'; - static const String EMAIL = 'email'; - static const String PASSWORD = 'password'; - static const String ACL = 'ACL'; - - create(String username, String password, [String emailAddress]) { - return ParseUser(username, password, emailAddress); - } - - /// Gets the current user from the server + /// Handles all the ParseObject responses /// - /// Current user is stored locally, but in case of a server update [bool] - /// fromServer can be called and an updated version of the [User] object will be - /// returned - getCurrentUserFromServer() async { - // We can't get the current user and session without a sessionId - if (_client.data.sessionId == null) return null; - - try { - Uri tempUri = Uri.parse(_client.data.serverUrl); - - Uri uri = Uri( - scheme: tempUri.scheme, - host: tempUri.host, - path: "${tempUri.path}/users/me"); - - final response = await _client - .get(uri, headers: {HEADER_SESSION_TOKEN: _client.data.sessionId}); - return _handleResponse(response, ParseApiRQ.currentUser); - } on Exception catch (e) { - return _handleException(e, ParseApiRQ.currentUser); - } - } - /// Gets the current user from storage - /// - /// Current user is stored locally, but in case of a server update [bool] - /// fromServer can be called and an updated version of the [User] object will be - /// returned - static currentUser() { - return _getUserFromLocalStore(); - } - - /// Registers a user on Parse Server - /// - /// After creating a new user via [Parse.create] call this method to register - /// that user on Parse - signUp() async { - try { - if (emailAddress == null) return null; - - Map bodyData = {}; - bodyData["email"] = emailAddress; - bodyData["password"] = password; - bodyData["username"] = username; - - Uri tempUri = Uri.parse(_client.data.serverUrl); - - Uri url = Uri( - scheme: tempUri.scheme, - host: tempUri.host, - path: "${tempUri.path}$path"); - - final response = await _client.post(url, - headers: { - HEADER_REVOCABLE_SESSION: "1", - }, - body: JsonEncoder().convert(bodyData)); - - _handleResponse(response, ParseApiRQ.signUp); - return this; - } on Exception catch (e) { - return _handleException(e, ParseApiRQ.signUp); + /// There are 4 probable outcomes from a Parse API call, + /// 1. Fail - [ParseResponse()] will be returned with further details + /// 2. Success but no results. [ParseResponse()] is returned. + /// 3. Success with simple OK. + /// 4. Success with results. Again [ParseResponse()] is returned + static handleResponse(dynamic object, Response apiResponse) { + var parseResponse = ParseResponse(); + + if (apiResponse != null) { + parseResponse.statusCode = apiResponse.statusCode; + + if (apiResponse.statusCode != 200 && apiResponse.statusCode != 201) { + return _handleError(parseResponse, apiResponse); + } else if (apiResponse.body == "{\"results\":[]}"){ + return _handleSuccessWithNoResults(parseResponse, 1, "Successful request, but no results found"); + } else if (apiResponse.body == "OK"){ + return _handleSuccessWithNoResults(parseResponse, 2, "Successful request"); + } else { + return _handleSuccess(parseResponse, object, apiResponse.body); + } + } else { + parseResponse.error = ParseError(message: "Error reaching server, or server response was null"); + return apiResponse; } } - /// Logs a user in via Parse - /// - /// Once a user is created using [Parse.create] and a username and password is - /// provided, call this method to login. - login() async { - try { - Uri tempUri = Uri.parse(_client.data.serverUrl); - - Uri url = Uri( - scheme: tempUri.scheme, - host: tempUri.host, - path: "${tempUri.path}/login", - queryParameters: {"username": username, "password": password}); - - final response = await _client.post(url, headers: { - HEADER_REVOCABLE_SESSION: "1", - }); - - _handleResponse(response, ParseApiRQ.login); - return this; - } on Exception catch (e) { - return _handleException(e, ParseApiRQ.login); - } + /// Handles exception instead of throwing an exception + static ParseResponse handleException(Exception exception) { + var response = ParseResponse(); + response.error = ParseError(message: exception.toString(), isTypeOfException: true); + return response; } - /// Removes the current user from the session data - logout() { - _client.data.sessionId = null; - setObjectData(null); + /// Handles any errors returned in response + static ParseResponse _handleError(ParseResponse response, Response apiResponse) { + Map responseData = JsonDecoder().convert(apiResponse.body); + response.error = ParseError(code: responseData['code'], message: responseData['error']); + response.statusCode = responseData['code']; + return response; } - /// Sends a verification email to the users email address - verificationEmailRequest() async { - try { - final response = await _client.post( - "${_client.data.serverUrl}/verificationEmailRequest", - body: JsonEncoder().convert({"email": emailAddress})); - - return _handleResponse(response, ParseApiRQ.verificationEmailRequest); - } on Exception catch (e) { - return _handleException(e, ParseApiRQ.verificationEmailRequest); - } + /// Handles successful responses with no results + static ParseResponse _handleSuccessWithNoResults(ParseResponse response, int code, String value) { + response.success = true; + response.statusCode = 200; + response.error = ParseError(code: code, message: value); + return response; } - /// Sends a password reset email to the users email address - requestPasswordReset() async { - try { - final response = await _client.post( - "${_client.data.serverUrl}/requestPasswordReset", - body: JsonEncoder().convert({"email": emailAddress})); + /// Handles successful response with results + static ParseResponse _handleSuccess(ParseResponse response, dynamic object, String responseBody) { + response.success = true; - return _handleResponse(response, ParseApiRQ.requestPasswordReset); - } on Exception catch (e) { - return _handleException(e, ParseApiRQ.requestPasswordReset); - } - } + var map = JsonDecoder().convert(responseBody) as Map; - /// Saves the current user - /// - /// If changes are made to the current user, call save to sync them with - /// Parse Server - save() async { - if (objectId == null) { - return signUp(); + if (map != null && map.length == 1 && map.containsKey('results')) { + response.result = _handleMultipleResults(object, map.entries.first.value); } else { - try { - final response = await _client.put( - _client.data.serverUrl + "$path/$objectId", - body: JsonEncoder().convert(getObjectData())); - return _handleResponse(response, ParseApiRQ.save); - } on Exception catch (e) { - return _handleException(e, ParseApiRQ.save); - } + response.result = _handleSingleResult(object, map); } - } - /// Removes a user from Parse Server locally and online - Future destroy() async { - if (objectId != null) { - try { - final response = await _client.delete( - _client.data.serverUrl + "$path/$objectId", - headers: {"X-Parse-Session-Token": _client.data.sessionId}); - return _handleResponse(response, ParseApiRQ.destroy); - } on Exception catch (e) { - return _handleException(e, ParseApiRQ.destroy); - } - } + return response; } - /// Gets a list of all users (limited return) - static all() async { - - var emptyUser = ParseUser(null, null, null); - - try { - final response = await ParseHTTPClient().get( - "${ParseCoreData().serverUrl}/$path"); - - ParseResponse parseResponse = ParseResponse.handleResponse(emptyUser, response); + /// Handles a response with a multiple result object + static _handleMultipleResults(dynamic object, dynamic map) { + var resultsList = List(); - if (ParseCoreData().debug) { - logger(ParseCoreData().appName, '_User', ParseApiRQ.getAll.toString(), parseResponse); - } - - return parseResponse; - } on Exception catch (e) { - return ParseResponse.handleException(e); + for (var value in map) { + resultsList.add(_handleSingleResult(object, value)); } - } - - static _getUserFromLocalStore() { - var userJson = ParseCoreData().getStore().getString(PARSE_STORE_USER); - if (userJson != null) { - var userMap = JsonDecoder().convert(userJson); - - if (userMap != null) { - ParseCoreData().sessionId = userMap['sessionToken']; - var user = ParseUser(null,null,null); - user.fromJson(userMap); - return user; - } - } - - return null; - } - - /// Handles an API response and logs data if [bool] debug is enabled - @protected - ParseResponse _handleException(Exception exception, ParseApiRQ type) { - ParseResponse parseResponse = ParseResponse.handleException(exception); - - if (_debug) { - logger(ParseCoreData().appName, className, type.toString(), parseResponse); - } - - return parseResponse; + return resultsList; } - /// Handles all the response data for this class - _handleResponse(Response response, ParseApiRQ type) { - - ParseResponse parseResponse = ParseResponse.handleResponse(this, response); - if (_debug) { - logger(ParseCoreData().appName, className, type.toString(), parseResponse); - } - - Map responseData = JsonDecoder().convert(response.body); - if (responseData.containsKey('objectId')) { - fromJson(responseData); - _client.data.sessionId = responseData['sessionToken']; - } - - if (type == ParseApiRQ.getAll || type == ParseApiRQ.destroy) { - return parseResponse; - } else { - saveInStorage(PARSE_STORE_USER); - return this; - } + /// Handles a response with a single result object + static _handleSingleResult(dynamic object, map) { + if (object is Parse) return map; + if (object is ParseCloneable) return object.clone(map); + return null; } } diff --git a/lib/src/utils/parse_decoder.dart b/lib/src/utils/parse_decoder.dart index 65b9c7364..9bb66ed5b 100644 --- a/lib/src/utils/parse_decoder.dart +++ b/lib/src/utils/parse_decoder.dart @@ -1,76 +1,74 @@ part of flutter_parse_sdk; -class ParseDecoder { +List _convertJSONArrayToList(List array) { + List list = new List(); + array.forEach((value) { + list.add(parseDecode(value)); + }); + return list; +} - List _convertJSONArrayToList(List array) { - List list = new List(); - array.forEach((value) { - list.add(decode(value)); - }); - return list; - } +Map _convertJSONObjectToMap(Map object) { + Map map = new Map(); + object.forEach((key, value) { + map.putIfAbsent(key, () => parseDecode(value)); + }); + return map; +} - Map _convertJSONObjectToMap(Map object) { - Map map = new Map(); - object.forEach((key, value) { - map.putIfAbsent(key, () => decode(value)); - }); - return map; +/// Decode any type value +dynamic parseDecode(dynamic value) { + if (value is List) { + return _convertJSONArrayToList(value); } - /// Decode any type value - dynamic decode(dynamic value) { - if (value is List) { - return _convertJSONArrayToList(value); - } - - if (value is bool) { - return value; - } - - if (value is int) { - return value.toInt(); - } + if (value is bool) { + return value; + } - if (value is double) { - return value.toDouble(); - } + if (value is int) { + return value.toInt(); + } - if (value is num) { - return value; - } + if (value is double) { + return value.toDouble(); + } - if (!(value is Map)) { - return value; - } + if (value is num) { + return value; + } - Map map = value; - if (!map.containsKey("__type")) { - return _convertJSONObjectToMap(map); - } + if (!(value is Map)) { + return value; + } - switch (map["__type"]) { - case "Date": - String iso = map["iso"]; - return stringToDateTime(iso); - case "Bytes": - String val = map["base64"]; - return base64.decode(val); - case "Pointer": - String className = map["className"]; - return ParseObject(className).fromJson(map); - case "Object": - String className = map["className"]; - if (className == '_User') { - return ParseUser(null,null,null).fromJson(map); - } - return ParseObject(className).fromJson(map); - case "GeoPoint": - num latitude = map["latitude"] ?? 0.0; - num longitude = map["longitude"] ?? 0.0; - return new ParseGeoPoint(latitude: latitude.toDouble(), longitude: longitude.toDouble()); - } + Map map = value; + if (!map.containsKey("__type")) { + return _convertJSONObjectToMap(map); + } - return null; + switch (map["__type"]) { + case "Date": + String iso = map["iso"]; + return DateTime.parse(iso); + case "Bytes": + String val = map["base64"]; + return base64.decode(val); + case "Pointer": + String className = map["className"]; + return ParseObject(className).fromJson(map); + case "Object": + String className = map["className"]; + if (className == '_User') { + return ParseUser(null, null, null).fromJson(map); + } + return ParseObject(className).fromJson(map); + case "GeoPoint": + num latitude = map["latitude"] ?? 0.0; + num longitude = map["longitude"] ?? 0.0; + return new ParseGeoPoint( + latitude: latitude.toDouble(), longitude: longitude.toDouble()); } -} \ No newline at end of file + + return null; +} diff --git a/lib/src/utils/parse_encoder.dart b/lib/src/utils/parse_encoder.dart index e1c41727f..4fc52a7ca 100644 --- a/lib/src/utils/parse_encoder.dart +++ b/lib/src/utils/parse_encoder.dart @@ -1,49 +1,55 @@ part of flutter_parse_sdk; -class ParseEncoder { - - bool isValidType(dynamic value) { - return value == null || - value is String || - value is num || - value is bool || - value is DateTime || - value is List || - value is Map || - value is ParseObject || - value is ParseGeoPoint || - value is ParseUser; +/// Custom encoder for DateTime +dynamic dateTimeEncoder(dynamic item) { + if(item is DateTime) { + return item.toIso8601String(); + } + return item; +} + +bool isValidType(dynamic value) { + return value == null || + value is String || + value is num || + value is bool || + value is DateTime || + value is List || + value is Map || + value is ParseObject || + value is ParseGeoPoint || + value is ParseUser; +} + +/// Custom json encoder for types related to parse +dynamic parseEncode(dynamic value) { + if (value is DateTime) return _encodeDate(value); + + if (value is List) { + return value.map((v) { + return parseEncode(v); + }).toList(); } - /// Custom json encoder for types related to parse - dynamic encode(dynamic value) { - if (value is DateTime) return _encodeDate(value); - - if (value is List) { - return value.map((v){ - return encode(v); - }).toList(); - } + if (value is ParseObject) { + return _encodeObject(value); + } - if (value is ParseObject) { - return value.toJson; - } + if (value is ParseUser) { + return value.toJson(); + } - if (value is ParseUser) { - return value.toJson; - } + if (value is ParseGeoPoint) { + return value.toJson; + } - if (value is ParseGeoPoint) { - return value.toJson; - } + return value; +} - return value; - } +String _encodeObject(ParseObject object){ + return "{'__type': 'Pointer', $keyVarClassName: ${object.className}, $keyVarObjectId: ${object.objectId}}"; +} - Map _encodeDate(DateTime date) { - return { - "__type": "Date", - "iso": dateTimeToString(date) - }; - } -} \ No newline at end of file +Map _encodeDate(DateTime date) { + return {"__type": "Date", "iso": date.toIso8601String()}; +} diff --git a/lib/src/utils/parse_logger.dart b/lib/src/utils/parse_logger.dart index b35df4129..9bb66ed5b 100644 --- a/lib/src/utils/parse_logger.dart +++ b/lib/src/utils/parse_logger.dart @@ -1,26 +1,74 @@ part of flutter_parse_sdk; -void logger( - String appName, - String className, - String type, - ParseResponse parseResponse) { - var responseString = ' \n'; - - responseString += "----\n$appName API Response ($className : $type) :"; - - if (parseResponse.success && parseResponse.result != null) { - responseString += "\nStatus Code: ${parseResponse.statusCode}"; - responseString += "\nPayload: ${parseResponse.result.toString()}"; - } else if (!parseResponse.success) { - responseString += "\nStatus Code: ${parseResponse.error.code}"; - responseString += "\nType: ${parseResponse.error.type}"; - - String errorOrException = parseResponse.error.isTypeOfException ? "Exception" : "Error"; - - responseString += "\n$errorOrException: ${parseResponse.error.message}"; - } - - responseString += "\n----\n"; - print(responseString); -} \ No newline at end of file +List _convertJSONArrayToList(List array) { + List list = new List(); + array.forEach((value) { + list.add(parseDecode(value)); + }); + return list; +} + +Map _convertJSONObjectToMap(Map object) { + Map map = new Map(); + object.forEach((key, value) { + map.putIfAbsent(key, () => parseDecode(value)); + }); + return map; +} + +/// Decode any type value +dynamic parseDecode(dynamic value) { + if (value is List) { + return _convertJSONArrayToList(value); + } + + if (value is bool) { + return value; + } + + if (value is int) { + return value.toInt(); + } + + if (value is double) { + return value.toDouble(); + } + + if (value is num) { + return value; + } + + if (!(value is Map)) { + return value; + } + + Map map = value; + if (!map.containsKey("__type")) { + return _convertJSONObjectToMap(map); + } + + switch (map["__type"]) { + case "Date": + String iso = map["iso"]; + return DateTime.parse(iso); + case "Bytes": + String val = map["base64"]; + return base64.decode(val); + case "Pointer": + String className = map["className"]; + return ParseObject(className).fromJson(map); + case "Object": + String className = map["className"]; + if (className == '_User') { + return ParseUser(null, null, null).fromJson(map); + } + return ParseObject(className).fromJson(map); + case "GeoPoint": + num latitude = map["latitude"] ?? 0.0; + num longitude = map["longitude"] ?? 0.0; + return new ParseGeoPoint( + latitude: latitude.toDouble(), longitude: longitude.toDouble()); + } + + return null; +} diff --git a/lib/src/utils/parse_utils.dart b/lib/src/utils/parse_utils.dart index 7a448b6db..7e0c84d80 100644 --- a/lib/src/utils/parse_utils.dart +++ b/lib/src/utils/parse_utils.dart @@ -4,7 +4,7 @@ part of flutter_parse_sdk; /// /// Debug can be set in 2 places, one global param in the Parse.initialise, and /// then can be overidden class by class -bool isDebugEnabled(ParseHTTPClient _client, {objectLevelDebug: false}) { +bool isDebugEnabled({objectLevelDebug: false}) { bool debug = objectLevelDebug; if (ParseCoreData().debug != null) debug = ParseCoreData().debug; return debug; diff --git a/pubspec.yaml b/pubspec.yaml index cfa535d1d..aa715c528 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.4 +version: 1.0.5 homepage: https://github.com/phillwiggins/flutter_parse_sdk author: PhillWiggins