diff --git a/lib/src/base/parse_constants.dart b/lib/src/base/parse_constants.dart index 194262336..f591f1416 100644 --- a/lib/src/base/parse_constants.dart +++ b/lib/src/base/parse_constants.dart @@ -10,6 +10,7 @@ const String keyEndPointLogin = '/login'; const String keyEndPointLogout = '/logout'; const String keyEndPointUsers = '/users'; const String keyEndPointSessions = '/sessions'; +const String keyEndPointInstallations = '/installations'; const String keyEndPointVerificationEmail = '/verificationEmailRequest'; const String keyEndPointRequestPasswordReset = '/requestPasswordReset'; const String keyEndPointClasses = '/classes/'; diff --git a/lib/src/objects/parse_base.dart b/lib/src/objects/parse_base.dart index ad51dabb1..bbb90c2a8 100644 --- a/lib/src/objects/parse_base.dart +++ b/lib/src/objects/parse_base.dart @@ -52,7 +52,7 @@ abstract class ParseBase { map.remove(keyVarAcl); map.remove(keyParamSessionToken); } - + return map; } diff --git a/lib/src/objects/parse_file.dart b/lib/src/objects/parse_file.dart index 31a6bd648..a9b3c87dd 100644 --- a/lib/src/objects/parse_file.dart +++ b/lib/src/objects/parse_file.dart @@ -36,7 +36,7 @@ class ParseFile extends ParseObject { if (file != null) { this.name = path.basename(file.path); - this._path = 'files/$name'; + this._path = '/files/$name'; } else { this.name = name; this.url = url; @@ -79,20 +79,37 @@ class ParseFile extends ParseObject { } /// Uploads a file to Parse Server - upload() async { + @override + Future save() async { + return upload(); + } + + /// Uploads a file to Parse Server + Future upload() async { if (saved) { - return this; + //Creates a Fake Response to return the correct result + final response = {"url": this.url, "name": this.name}; + return handleResponse(this, Response(json.encode(response), 201), + ParseApiRQ.upload, _debug, className); } final ext = path.extension(file.path).replaceAll('.', ''); final headers = { HttpHeaders.contentTypeHeader: getContentType(ext) }; - - var uri = _client.data.serverUrl + "$_path"; - final body = await file.readAsBytes(); - final response = await _client.post(uri, headers: headers, body: body); - return handleResponse( - this, response, ParseApiRQ.upload, _debug, className); + try { + var uri = _client.data.serverUrl + "$_path"; + final body = await file.readAsBytes(); + final response = await _client.post(uri, headers: headers, body: body); + if (response.statusCode == 201) { + final map = json.decode(response.body); + this.url = map["url"].toString(); + this.name = map["name"].toString(); + } + return handleResponse( + this, response, ParseApiRQ.upload, _debug, className); + } on Exception catch (e) { + return handleException(e, ParseApiRQ.upload, _debug, className); + } } } diff --git a/lib/src/objects/parse_geo_point.dart b/lib/src/objects/parse_geo_point.dart index e541fcefb..076691529 100644 --- a/lib/src/objects/parse_geo_point.dart +++ b/lib/src/objects/parse_geo_point.dart @@ -36,4 +36,11 @@ class ParseGeoPoint extends ParseObject { assert(value >= -180.0 || value <= 180.0); _longitude = value; } + + @override + toJson({bool full: false, bool forApiRQ: false}) => { + "__type": "GeoPoint", + "latitude": _latitude, + "longitude": _longitude + }; } diff --git a/lib/src/objects/parse_installation.dart b/lib/src/objects/parse_installation.dart index 32d148f9c..7e16f1664 100644 --- a/lib/src/objects/parse_installation.dart +++ b/lib/src/objects/parse_installation.dart @@ -10,7 +10,8 @@ class ParseInstallation extends ParseObject { static final String keyAppVersion = 'appVersion'; static final String keyAppIdentifier = 'appIdentifier'; static final String keyParseVersion = 'parseVersion'; - static final List readOnlyKeys = [ //TODO + static final List readOnlyKeys = [ + //TODO keyDeviceToken, keyDeviceType, keyInstallationId, keyAppName, keyAppVersion, keyAppIdentifier, keyParseVersion ]; @@ -24,13 +25,15 @@ class ParseInstallation extends ParseObject { String get deviceToken => super.get(keyDeviceToken); - set deviceToken(String deviceToken) => set(keyDeviceToken, deviceToken); + set deviceToken(String deviceToken) => + set(keyDeviceToken, deviceToken); String get deviceType => super.get(keyDeviceType); String get installationId => super.get(keyInstallationId); - set _installationId(String installationId) => set(keyInstallationId, installationId); + set _installationId(String installationId) => + set(keyInstallationId, installationId); String get appName => super.get(keyAppName); @@ -42,9 +45,7 @@ class ParseInstallation extends ParseObject { /// Creates an instance of ParseInstallation ParseInstallation( - {bool debug, - ParseHTTPClient client, - bool autoSendSessionId}) + {bool debug, ParseHTTPClient client, bool autoSendSessionId}) : super(keyClassInstallation) { _debug = isDebugEnabled(objectLevelDebug: debug); _client = client ?? @@ -60,7 +61,8 @@ class ParseInstallation extends ParseObject { if (_currentInstallationId == null) { _currentInstallationId = (await _getFromLocalStore()).installationId; } - return _currentInstallationId != null && installation.installationId == _currentInstallationId; + return _currentInstallationId != null && + installation.installationId == _currentInstallationId; } /// Gets the current installation from storage @@ -75,9 +77,12 @@ class ParseInstallation extends ParseObject { /// Updates the installation with current device data _updateInstallation() async { //Device type - if (Platform.isAndroid) set(keyDeviceType, "android"); - else if (Platform.isIOS) set(keyDeviceType, "ios"); - else throw Exception("Unsupported platform/operating system"); + if (Platform.isAndroid) + set(keyDeviceType, "android"); + else if (Platform.isIOS) + set(keyDeviceType, "ios"); + else + throw Exception("Unsupported platform/operating system"); //Locale String locale = await Devicelocale.currentLocale; @@ -99,7 +104,8 @@ class ParseInstallation extends ParseObject { Future create() async { var isCurrent = await ParseInstallation.isCurrent(this); if (isCurrent) await _updateInstallation(); - ParseResponse parseResponse = await super.create(); + //ParseResponse parseResponse = await super.create(); + ParseResponse parseResponse = await _create(); if (parseResponse.success && isCurrent) { saveInStorage(keyParseStoreInstallation); } @@ -110,7 +116,8 @@ class ParseInstallation extends ParseObject { Future save() async { var isCurrent = await ParseInstallation.isCurrent(this); if (isCurrent) await _updateInstallation(); - ParseResponse parseResponse = await super.save(); + //ParseResponse parseResponse = await super.save(); + ParseResponse parseResponse = await _save(); if (parseResponse.success && isCurrent) { saveInStorage(keyParseStoreInstallation); } @@ -145,4 +152,76 @@ class ParseInstallation extends ParseObject { await installation._updateInstallation(); return installation; } + + /// Creates a new object and saves it online + Future _create() async { + try { + var uri = _client.data.serverUrl + "$keyEndPointInstallations"; + var body = json.encode(toJson(forApiRQ: true)); + if (_debug) { + logRequest(ParseCoreData().appName, className, + ParseApiRQ.create.toString(), uri, body); + } + var result = await _client.post(uri, body: body); + + //Set the objectId on the object after it is created. + //This allows you to perform operations on the object after creation + if (result.statusCode == 201) { + final map = json.decode(result.body); + this.objectId = map["objectId"].toString(); + } + + return handleResponse(this, result, ParseApiRQ.create, _debug, className); + } on Exception catch (e) { + return handleException(e, ParseApiRQ.create, _debug, className); + } + } + + /// Saves the current object online + Future _save() async { + if (getObjectData()[keyVarObjectId] == null) { + return create(); + } else { + try { + var uri = + "${ParseCoreData().serverUrl}$keyEndPointInstallations/$objectId"; + var body = json.encode(toJson(forApiRQ: true)); + if (_debug) { + logRequest(ParseCoreData().appName, className, + ParseApiRQ.save.toString(), uri, body); + } + var result = await _client.put(uri, body: body); + return handleResponse(this, result, ParseApiRQ.save, _debug, className); + } on Exception catch (e) { + return handleException(e, ParseApiRQ.save, _debug, className); + } + } + } + + ///Subscribes the device to a channel of push notifications. + void subscribeToChannel(String value) { + final List channel = [value]; + this.addUnique("channels", channel); + } + + ///Unsubscribes the device to a channel of push notifications. + void unsubscribeFromChannel(String value) { + final List channel = [value]; + this.removeAll("channels", channel); + } + + ///Returns an > containing all the channel names this device is subscribed to. + Future> getSubscribedChannels() async { + print("getSubscribedChannels"); + final apiResponse = + await ParseObject(keyClassInstallation).getObject(this.objectId); + + if (apiResponse.success) { + var installation = apiResponse.result as ParseObject; + print("achou installation"); + return Future.value(installation.get>("channels")); + } else { + return null; + } + } } diff --git a/lib/src/objects/parse_object.dart b/lib/src/objects/parse_object.dart index 8bc5b06ca..61c13c918 100644 --- a/lib/src/objects/parse_object.dart +++ b/lib/src/objects/parse_object.dart @@ -29,7 +29,8 @@ class ParseObject extends ParseBase implements ParseCloneable { securityContext: ParseCoreData().securityContext); } - String toPointer() => parseEncode(this); + /// Converts the object to a Pointer to be used ONLY in queries using Pointers + dynamic toPointer() => json.encode(parseEncode(this)); /// Gets an object from the server using it's [String] objectId Future getObject(String objectId) async { @@ -58,7 +59,19 @@ class ParseObject extends ParseBase implements ParseCloneable { try { var uri = _client.data.serverUrl + "$_path"; var body = json.encode(toJson(forApiRQ: true)); + if (_debug) { + logRequest(ParseCoreData().appName, className, + ParseApiRQ.create.toString(), uri, body); + } var result = await _client.post(uri, body: body); + + //Set the objectId on the object after it is created. + //This allows you to perform operations on the object after creation + if (result.statusCode == 201) { + final map = json.decode(result.body); + this.objectId = map["objectId"].toString(); + } + return handleResponse(this, result, ParseApiRQ.create, _debug, className); } on Exception catch (e) { return handleException(e, ParseApiRQ.create, _debug, className); @@ -73,6 +86,10 @@ class ParseObject extends ParseBase implements ParseCloneable { try { var uri = "${ParseCoreData().serverUrl}$_path/$objectId"; var body = json.encode(toJson(forApiRQ: true)); + if (_debug) { + logRequest(ParseCoreData().appName, className, + ParseApiRQ.save.toString(), uri, body); + } var result = await _client.put(uri, body: body); return handleResponse(this, result, ParseApiRQ.save, _debug, className); } on Exception catch (e) { @@ -82,6 +99,7 @@ class ParseObject extends ParseBase implements ParseCloneable { } /// Removes an element from an Array + @Deprecated('Prefer to use the setRemove() method in save()') Future remove(String key, dynamic values) async { if (key != null) { return await _sortArrays(ParseApiRQ.remove, "Remove", key, values); @@ -90,7 +108,13 @@ class ParseObject extends ParseBase implements ParseCloneable { } } + /// Removes an element from an Array + void setRemove(String key, dynamic values) { + _arrayOperation("Remove", key, values); + } + /// Remove multiple elements from an array of an object + @Deprecated('Prefer to use the setRemoveAll() method in save()') Future removeAll(String key, List values) async { if (key != null) { return await _sortArrays(ParseApiRQ.removeAll, "Remove", key, values); @@ -99,7 +123,13 @@ class ParseObject extends ParseBase implements ParseCloneable { } } + /// Remove multiple elements from an array of an object + void setRemoveAll(String key, List values) { + _arrayOperation("Remove", key, values); + } + /// Add a multiple elements to an array of an object + @Deprecated('Prefer to use the setAddAll() method in save()') Future addAll(String key, List values) async { if (key != null) { return await _sortArrays(ParseApiRQ.addAll, "Add", key, values); @@ -108,7 +138,13 @@ class ParseObject extends ParseBase implements ParseCloneable { } } + /// Add a multiple elements to an array of an object + void setAddAll(String key, List values) { + _arrayOperation("Add", key, values); + } + /// Add a multiple elements to an array of an object, but only when they are unique + @Deprecated('Prefer to use the setAddAll() method in save()') Future addUnique(String key, List values) async { if (key != null) { return await _sortArrays(ParseApiRQ.addUnique, "AddUnique", key, values); @@ -117,8 +153,14 @@ class ParseObject extends ParseBase implements ParseCloneable { } } + /// Add a multiple elements to an array of an object + void setAddUnique(String key, List values) { + _arrayOperation("AddUnique", key, values); + } + /// Add a single element to an array of an object - Future add(String key, List values) async { + @Deprecated('Prefer to use the setAdd() method in save()') + Future add(String key, dynamic values) async { if (key != null) { return await _sortArrays(ParseApiRQ.add, "Add", key, values); } else { @@ -126,6 +168,11 @@ class ParseObject extends ParseBase implements ParseCloneable { } } + /// Add a single element to an array of an object + void setAdd(String key, dynamic values) { + _arrayOperation("Add", key, values); + } + /// Can be used to add arrays to a given type Future _sortArrays(ParseApiRQ apiRQType, String arrayAction, String key, List values) async { @@ -144,7 +191,14 @@ class ParseObject extends ParseBase implements ParseCloneable { } } + /// Used in array Operations in save() method + void _arrayOperation(String arrayAction, String key, List values) { + this.set>( + key, {'__op': arrayAction, 'objects': values}); + } + /// Increases a num of an object by x amount + @Deprecated('Prefer to use the setIncrement() method in save()') Future increment(String key, num amount) async { if (key != null) { return await _increment(ParseApiRQ.increment, "Increment", key, amount); @@ -153,7 +207,14 @@ class ParseObject extends ParseBase implements ParseCloneable { } } + /// Increases a num of an object by x amount + void setIncrement(String key, num amount) { + this.set>( + key, {'__op': 'Increment', 'amount': amount}); + } + /// Decreases a num of an object by x amount + @Deprecated('Prefer to use the setDecrement() method in save()') Future decrement(String key, num amount) async { if (key != null) { return await _increment(ParseApiRQ.decrement, "Increment", key, -amount); @@ -162,15 +223,21 @@ class ParseObject extends ParseBase implements ParseCloneable { } } + /// Decreases a num of an object by x amount + void setDecrement(String key, num amount) { + this.set>( + key, {'__op': 'Increment', 'amount': -amount}); + } + /// Can be used to add arrays to a given type Future _increment( ParseApiRQ apiRQType, String countAction, String key, num amount) async { try { if (objectId != null) { - var uri = "${ParseCoreData().serverUrl}$_path/$objectId"; - var body = "{\"$key\":{\"__op\":\"$countAction\",\"amount\":$amount}}"; - var result = await _client.put(uri, body: body); - return handleResponse(this, result, apiRQType, _debug, className); + var uri = "${ParseCoreData().serverUrl}$_path/$objectId"; + var body = "{\"$key\":{\"__op\":\"$countAction\",\"amount\":$amount}}"; + var result = await _client.put(uri, body: body); + return handleResponse(this, result, apiRQType, _debug, className); } else { return null; } @@ -202,6 +269,10 @@ class ParseObject extends ParseBase implements ParseCloneable { try { path ??= _path; var uri = "${ParseCoreData().serverUrl}$path/$objectId"; + if (_debug) { + logRequest(ParseCoreData().appName, className, + ParseApiRQ.delete.toString(), uri, ""); + } var result = await _client.delete(uri); return handleResponse(this, result, ParseApiRQ.delete, _debug, className); } on Exception catch (e) { diff --git a/lib/src/objects/parse_session.dart b/lib/src/objects/parse_session.dart index 8fd0d9a48..1bd2fd88b 100644 --- a/lib/src/objects/parse_session.dart +++ b/lib/src/objects/parse_session.dart @@ -3,7 +3,6 @@ part of flutter_parse_sdk; class ParseSession extends ParseObject implements ParseCloneable { @override clone(Map map) { - print(map); return this.fromJson(map); } diff --git a/lib/src/objects/parse_user.dart b/lib/src/objects/parse_user.dart index b773431a4..f50770c35 100644 --- a/lib/src/objects/parse_user.dart +++ b/lib/src/objects/parse_user.dart @@ -232,9 +232,7 @@ class ParseUser extends ParseObject implements ParseCloneable { keyHeaderRevocableSession: "1", }, body: jsonEncode({ - "authData": { - provider: authData - } + "authData": {provider: authData} })); return _handleResponse( @@ -424,4 +422,11 @@ class ParseUser extends ParseObject implements ParseCloneable { } static ParseUser _getEmptyUser() => ParseUser(null, null, null); + + @override + toJson({bool full: false, bool forApiRQ: false}) => { + "__type": "Pointer", + keyVarClassName: keyClassUser, + keyVarObjectId: this.objectId + }; } diff --git a/lib/src/utils/parse_encoder.dart b/lib/src/utils/parse_encoder.dart index ef0e564a9..8e4c11766 100644 --- a/lib/src/utils/parse_encoder.dart +++ b/lib/src/utils/parse_encoder.dart @@ -12,49 +12,49 @@ dynamic dateTimeEncoder(dynamic item) { dynamic parseEncode(dynamic value, {bool full}) { full ??= false; - if (value is DateTime) return _encodeDate(value); - - if (value is List) { - return value.map((v) { - return parseEncode(v); - }).toList(); - } - - if (value is ParseObject) { - if (full) { - return value.toJson(full: full); - } else { - return _encodeObject(value); - } + if (value is DateTime) { + return _encodeDate(value); } - if (value is ParseUser) { - return value.toJson(); + if (value is Uint8List) { + return _encodeUint8List(value); } if (value is ParseGeoPoint) { - return value.toJson; + return value; } if (value is ParseFile) { - return value.toJson; + return value; } - if (value is Uint8List) { - return _encodeUint8List(value); + if (value is ParseUser) { + return value; + } + + if (value is ParseObject) { + if (full) { + return value.toJson(full: full); + } else { + return _encodeObject(value); + } } return value; } -String _encodeUint8List(Uint8List value) { - return "{\"__type\": \"Bytes\", \"base64\": \"${base64.encode(value)}\"}"; +Map _encodeUint8List(Uint8List value) { + return {"__type": "Bytes", "base64": base64.encode(value)}; } -String _encodeObject(ParseObject object) { - return "{\"__type\": \"Pointer\", \"$keyVarClassName\": \"${object.className}\", \"$keyVarObjectId\": \"${object.objectId}\"}"; +Map _encodeObject(ParseObject object) { + return { + "__type": "Pointer", + keyVarClassName: object.className, + keyVarObjectId: object.objectId + }; } -String _encodeDate(DateTime date) { - return "{\"__type\": \"Date\", \"iso\": \"${date.toIso8601String()}\"}"; +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 ff43b9ee2..7ce7eb7ef 100644 --- a/lib/src/utils/parse_logger.dart +++ b/lib/src/utils/parse_logger.dart @@ -28,3 +28,17 @@ void logger(String appName, String className, String type, responseString += "\n----\n"; print(responseString); } + +void logRequest( + String appName, String className, String type, String uri, String body) { + var requestString = ' \n'; + var name = appName; + if (name.length > 0) name = "$appName "; + + requestString += "----\n${name}API Request ($className : $type) :"; + requestString += "\nUri: ${uri}"; + requestString += "\nBody: ${body}"; + + requestString += "\n----\n"; + print(requestString); +}