From 06bbe9a5cc4b156a1a80588c7b99069bc75aaff0 Mon Sep 17 00:00:00 2001 From: maaeps Date: Fri, 7 Aug 2020 21:10:10 +0200 Subject: [PATCH 1/3] created files and started implementation --- lib/parse_server_sdk.dart | 47 +++---------------- lib/src/objects/parse_file.dart | 48 ++++---------------- lib/src/objects/parse_file_base.dart | 47 +++++++++++++++++++ lib/src/objects/parse_file_web.dart | 68 ++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+), 78 deletions(-) create mode 100644 lib/src/objects/parse_file_base.dart create mode 100644 lib/src/objects/parse_file_web.dart diff --git a/lib/parse_server_sdk.dart b/lib/parse_server_sdk.dart index 74029eff3..93fca240f 100644 --- a/lib/parse_server_sdk.dart +++ b/lib/parse_server_sdk.dart @@ -22,81 +22,48 @@ import 'package:uuid/uuid.dart'; import 'package:xxtea/xxtea.dart'; export 'src/network/parse_live_query.dart' -if (dart.library.js) 'src/network/parse_live_query_web.dart'; + if (dart.library.js) 'src/network/parse_live_query_web.dart'; export 'src/utils/parse_live_list.dart'; +part 'package:parse_server_sdk/src/data/core_store.dart'; part 'package:parse_server_sdk/src/data/parse_subclass_handler.dart'; part 'package:parse_server_sdk/src/objects/response/parse_error_response.dart'; - part 'package:parse_server_sdk/src/objects/response/parse_exception_response.dart'; - part 'package:parse_server_sdk/src/objects/response/parse_response_builder.dart'; - part 'package:parse_server_sdk/src/objects/response/parse_response_utils.dart'; - part 'package:parse_server_sdk/src/objects/response/parse_success_no_results.dart'; - -part 'package:parse_server_sdk/src/data/core_store.dart'; - part 'package:parse_server_sdk/src/storage/core_store_sem_impl.dart'; - part 'package:parse_server_sdk/src/storage/core_store_sp_impl.dart'; - part 'package:parse_server_sdk/src/storage/xxtea_codec.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_query.dart'; - part 'src/objects/parse_acl.dart'; - part 'src/objects/parse_base.dart'; - part 'src/objects/parse_cloneable.dart'; - part 'src/objects/parse_config.dart'; - part 'src/objects/parse_error.dart'; - part 'src/objects/parse_file.dart'; - +part 'src/objects/parse_file_base.dart'; +part 'src/objects/parse_file_web.dart'; part 'src/objects/parse_function.dart'; - part 'src/objects/parse_geo_point.dart'; - part 'src/objects/parse_installation.dart'; - +part 'src/objects/parse_merge.dart'; part 'src/objects/parse_object.dart'; - part 'src/objects/parse_relation.dart'; - part 'src/objects/parse_response.dart'; - part 'src/objects/parse_session.dart'; - part 'src/objects/parse_user.dart'; - part 'src/utils/parse_date_format.dart'; - part 'src/utils/parse_decoder.dart'; - part 'src/utils/parse_encoder.dart'; - part 'src/utils/parse_file_extensions.dart'; - part 'src/utils/parse_logger.dart'; - -part 'src/utils/parse_utils.dart'; - part 'src/utils/parse_login_helpers.dart'; - -part 'src/objects/parse_merge.dart'; +part 'src/utils/parse_utils.dart'; class Parse { ParseCoreData data; @@ -163,7 +130,7 @@ class Parse { final ParseHTTPClient _client = client ?? ParseHTTPClient( sendSessionId: - sendSessionIdByDefault ?? ParseCoreData().autoSendSessionId, + sendSessionIdByDefault ?? ParseCoreData().autoSendSessionId, securityContext: ParseCoreData().securityContext); const String className = 'parseBase'; diff --git a/lib/src/objects/parse_file.dart b/lib/src/objects/parse_file.dart index 4a0058de9..c44d63ab3 100644 --- a/lib/src/objects/parse_file.dart +++ b/lib/src/objects/parse_file.dart @@ -1,6 +1,6 @@ part of flutter_parse_sdk; -class ParseFile extends ParseObject { +class ParseFile extends ParseFileBase { /// Creates a new file /// /// {https://docs.parseplatform.org/rest/guide/#files/} @@ -9,41 +9,17 @@ class ParseFile extends ParseObject { String url, bool debug, ParseHTTPClient client, - bool autoSendSessionId}) - : super('ParseFile', debug: debug, autoSendSessionId: autoSendSessionId) { - _debug = isDebugEnabled(objectLevelDebug: debug); - _client = client ?? - ParseHTTPClient( - sendSessionId: - autoSendSessionId ?? ParseCoreData().autoSendSessionId, - securityContext: ParseCoreData().securityContext); - - if (file != null) { - name = path.basename(file.path); - _path = '/files/$name'; - } else { - name = name; - url = url; - } - } + bool autoSendSessionId}) + : super( + name: file != null ? path.basename(file.path) : name, + url: url, + debug: debug, + client: client, + autoSendSessionId: autoSendSessionId, + ); File file; - String get name => super.get(keyVarName); - set name(String name) => set(keyVarName, name); - - String get url => super.get(keyVarURL); - set url(String url) => set(keyVarURL, url); - - bool get saved => url != null; - - @override - Map toJson({bool full = false, bool forApiRQ = false}) => - {'__type': keyFile, 'name': name, 'url': url}; - - @override - String toString() => json.encode(toJson(full: true)); - Future loadStorage() async { final Directory tempPath = await getTemporaryDirectory(); @@ -65,6 +41,7 @@ class ParseFile extends ParseObject { return this; } + @override Future download() async { if (url == null) { return this; @@ -81,11 +58,6 @@ class ParseFile extends ParseObject { /// Uploads a file to Parse Server @override - Future save() async { - return upload(); - } - - /// Uploads a file to Parse Server Future upload() async { if (saved) { //Creates a Fake Response to return the correct result diff --git a/lib/src/objects/parse_file_base.dart b/lib/src/objects/parse_file_base.dart new file mode 100644 index 000000000..882e7cd98 --- /dev/null +++ b/lib/src/objects/parse_file_base.dart @@ -0,0 +1,47 @@ +part of flutter_parse_sdk; + +abstract class ParseFileBase extends ParseObject { + /// Creates a new file + /// + /// {https://docs.parseplatform.org/rest/guide/#files/} + ParseFileBase( + {@required String name, + String url, + bool debug, + ParseHTTPClient client, + bool autoSendSessionId}) + : super('ParseFile', + debug: debug, + autoSendSessionId: autoSendSessionId, + client: client) { + _path = '/files/$name'; + this.name = name; + this.url = url; + } + + String get name => super.get(keyVarName); + set name(String name) => set(keyVarName, name); + + String get url => super.get(keyVarURL); + set url(String url) => set(keyVarURL, url); + + bool get saved => url != null; + + @override + Map toJson({bool full = false, bool forApiRQ = false}) => + {'__type': keyFile, 'name': name, 'url': url}; + + @override + String toString() => json.encode(toJson(full: true)); + + /// Uploads a file to Parse Server + @override + Future save() async { + return upload(); + } + + /// Uploads a file to Parse Server + Future upload(); + + Future download(); +} diff --git a/lib/src/objects/parse_file_web.dart b/lib/src/objects/parse_file_web.dart new file mode 100644 index 000000000..01f4feb2f --- /dev/null +++ b/lib/src/objects/parse_file_web.dart @@ -0,0 +1,68 @@ +part of flutter_parse_sdk; + +class ParseWebFile extends ParseFileBase { + ParseWebFile(this.file, + {@required String name, + @required this.extension, + String url, + bool debug, + ParseHTTPClient client, + bool autoSendSessionId}) + : super( + name: name, + url: url, + debug: debug, + client: client, + autoSendSessionId: autoSendSessionId, + ); + + Uint8List file; + final String extension; + + @override + Future download() async { + if (url == null) { + return this; + } + + final Response response = await _client.get(url); + file = response.bodyBytes; + + return this; + } + + @override + Future upload() async { + if (saved) { + //Creates a Fake Response to return the correct result + final Map response = { + 'url': url, + 'name': name + }; + return handleResponse( + this, + Response(json.encode(response), 201), + ParseApiRQ.upload, + _debug, + parseClassName); + } + + final Map headers = { + HttpHeaders.contentTypeHeader: getContentType(extension) + }; + try { + final String uri = _client.data.serverUrl + '$_path'; + final Response response = + await _client.post(uri, headers: headers, body: file); + if (response.statusCode == 201) { + final Map map = json.decode(response.body); + url = map['url'].toString(); + name = map['name'].toString(); + } + return handleResponse( + this, response, ParseApiRQ.upload, _debug, parseClassName); + } on Exception catch (e) { + return handleException(e, ParseApiRQ.upload, _debug, parseClassName); + } + } +} From dd1cb5cd20ddbae32e627ad44b2bc6e5e0ffde43 Mon Sep 17 00:00:00 2001 From: maaeps Date: Fri, 7 Aug 2020 21:37:11 +0200 Subject: [PATCH 2/3] add different filetypes to sdk --- lib/src/base/parse_constants.dart | 1 + lib/src/data/parse_core_data.dart | 36 ++++++++++++++++-------- lib/src/data/parse_subclass_handler.dart | 35 ++++++++++++++++++----- lib/src/objects/parse_file_base.dart | 2 +- lib/src/objects/parse_file_web.dart | 8 ++---- lib/src/objects/parse_object.dart | 10 +++---- lib/src/utils/parse_decoder.dart | 5 ++-- lib/src/utils/parse_encoder.dart | 2 +- 8 files changed, 66 insertions(+), 33 deletions(-) diff --git a/lib/src/base/parse_constants.dart b/lib/src/base/parse_constants.dart index 9c1cb582f..c95f46f9e 100644 --- a/lib/src/base/parse_constants.dart +++ b/lib/src/base/parse_constants.dart @@ -39,6 +39,7 @@ const String keyClassInstallation = '_Installation'; const String keyGeoPoint = 'GeoPoint'; const String keyFile = 'File'; const String keyRelation = 'Relation'; +const String keyFileClassname = 'ParseFile'; // Headers const String keyHeaderSessionToken = 'X-Parse-Session-Token'; diff --git a/lib/src/data/parse_core_data.dart b/lib/src/data/parse_core_data.dart index 22bc19a47..47b7f1a02 100644 --- a/lib/src/data/parse_core_data.dart +++ b/lib/src/data/parse_core_data.dart @@ -14,18 +14,22 @@ class ParseCoreData { /// /// This class should not be user unless switching servers during the app, /// which is odd. Should only be user by Parse.init - static Future init(String appId, String serverUrl, - {bool debug, - String appName, - String liveQueryUrl, - String masterKey, - String clientKey, - String sessionId, - bool autoSendSessionId, - SecurityContext securityContext, - CoreStore store, - Map registeredSubClassMap, - ParseUserConstructor parseUserConstructor}) async { + static Future init( + String appId, + String serverUrl, { + bool debug, + String appName, + String liveQueryUrl, + String masterKey, + String clientKey, + String sessionId, + bool autoSendSessionId, + SecurityContext securityContext, + CoreStore store, + Map registeredSubClassMap, + ParseUserConstructor parseUserConstructor, + ParseFileConstructor parseFileConstructor, + }) async { _instance = ParseCoreData._init(appId, serverUrl); _instance.storage ??= @@ -59,6 +63,7 @@ class ParseCoreData { _instance._subClassHandler = ParseSubClassHandler( registeredSubClassMap: registeredSubClassMap, parseUserConstructor: parseUserConstructor, + parseFileConstructor: parseFileConstructor, ); } @@ -84,6 +89,10 @@ class ParseCoreData { _subClassHandler.registerUserSubClass(parseUserConstructor); } + void registerFileSubClass(ParseFileConstructor parseFileConstructor) { + _subClassHandler.registerFileSubClass(parseFileConstructor); + } + ParseObject createObject(String classname) { return _subClassHandler.createObject(classname); } @@ -95,6 +104,9 @@ class ParseCoreData { sessionToken: sessionToken, debug: debug, client: client); } + ParseFileBase createFile({String url, String name}) => + _subClassHandler.createFile(name: name, url: url); + /// Sets the current sessionId. /// /// This is generated when a users logs in, or calls currentUser to update diff --git a/lib/src/data/parse_subclass_handler.dart b/lib/src/data/parse_subclass_handler.dart index 6f2a8db49..1ee161167 100644 --- a/lib/src/data/parse_subclass_handler.dart +++ b/lib/src/data/parse_subclass_handler.dart @@ -4,28 +4,46 @@ typedef ParseObjectConstructor = ParseObject Function(); typedef ParseUserConstructor = ParseUser Function( String username, String password, String emailAddress, {String sessionToken, bool debug, ParseHTTPClient client}); +typedef ParseFileConstructor = ParseFileBase Function( + {String name, String url}); class ParseSubClassHandler { - - ParseSubClassHandler({Map registeredSubClassMap, - ParseUserConstructor parseUserConstructor}){ - _subClassMap = registeredSubClassMap ?? Map(); + ParseSubClassHandler( + {Map registeredSubClassMap, + ParseUserConstructor parseUserConstructor, + ParseFileConstructor parseFileConstructor}) { + _subClassMap = + registeredSubClassMap ?? Map(); _parseUserConstructor = parseUserConstructor; + if (parseFileConstructor != null) + _parseFileConstructor = parseFileConstructor; } Map _subClassMap; ParseUserConstructor _parseUserConstructor; + ParseFileConstructor _parseFileConstructor = ({String name, String url}) { + if (kIsWeb) { + return ParseWebFile(null, name: name, url: url); + } else { + return ParseFile(null, name: name, url: url); + } + }; void registerSubClass( String className, ParseObjectConstructor objectConstructor) { if (className != keyClassUser && className != keyClassInstallation && - className != keyClassSession) - _subClassMap.putIfAbsent(className, () => objectConstructor); + className != keyClassSession && + className != keyFileClassname) + _subClassMap[className] = objectConstructor; } void registerUserSubClass(ParseUserConstructor parseUserConstructor) { - _parseUserConstructor ??= parseUserConstructor; + _parseUserConstructor = parseUserConstructor; + } + + void registerFileSubClass(ParseFileConstructor parseFileConstructor) { + _parseFileConstructor = parseFileConstructor; } ParseObject createObject(String classname) { @@ -43,4 +61,7 @@ class ParseSubClassHandler { : ParseUser(username, password, emailAddress, sessionToken: sessionToken, debug: debug, client: client); } + + ParseFileBase createFile({String name, String url}) => + _parseFileConstructor(name: name, url: url); } diff --git a/lib/src/objects/parse_file_base.dart b/lib/src/objects/parse_file_base.dart index 882e7cd98..e565b92a7 100644 --- a/lib/src/objects/parse_file_base.dart +++ b/lib/src/objects/parse_file_base.dart @@ -10,7 +10,7 @@ abstract class ParseFileBase extends ParseObject { bool debug, ParseHTTPClient client, bool autoSendSessionId}) - : super('ParseFile', + : super(keyFileClassname, debug: debug, autoSendSessionId: autoSendSessionId, client: client) { diff --git a/lib/src/objects/parse_file_web.dart b/lib/src/objects/parse_file_web.dart index 01f4feb2f..a5684133e 100644 --- a/lib/src/objects/parse_file_web.dart +++ b/lib/src/objects/parse_file_web.dart @@ -3,7 +3,6 @@ part of flutter_parse_sdk; class ParseWebFile extends ParseFileBase { ParseWebFile(this.file, {@required String name, - @required this.extension, String url, bool debug, ParseHTTPClient client, @@ -17,7 +16,6 @@ class ParseWebFile extends ParseFileBase { ); Uint8List file; - final String extension; @override Future download() async { @@ -39,7 +37,7 @@ class ParseWebFile extends ParseFileBase { 'url': url, 'name': name }; - return handleResponse( + return handleResponse( this, Response(json.encode(response), 201), ParseApiRQ.upload, @@ -48,7 +46,7 @@ class ParseWebFile extends ParseFileBase { } final Map headers = { - HttpHeaders.contentTypeHeader: getContentType(extension) + HttpHeaders.contentTypeHeader: getContentType(path.extension(url)) }; try { final String uri = _client.data.serverUrl + '$_path'; @@ -59,7 +57,7 @@ class ParseWebFile extends ParseFileBase { url = map['url'].toString(); name = map['name'].toString(); } - return handleResponse( + return handleResponse( this, response, ParseApiRQ.upload, _debug, parseClassName); } on Exception catch (e) { return handleException(e, ParseApiRQ.upload, _debug, parseClassName); diff --git a/lib/src/objects/parse_object.dart b/lib/src/objects/parse_object.dart index 4aaf07281..94d9a1576 100644 --- a/lib/src/objects/parse_object.dart +++ b/lib/src/objects/parse_object.dart @@ -121,7 +121,7 @@ class ParseObject extends ParseBase implements ParseCloneable { Future _saveChildren(dynamic object) async { final Set uniqueObjects = Set(); - final Set uniqueFiles = Set(); + final Set uniqueFiles = Set(); if (!_collectionDirtyChildren(object, uniqueObjects, uniqueFiles, Set(), Set())) { final ParseResponse response = ParseResponse(); @@ -130,7 +130,7 @@ class ParseObject extends ParseBase implements ParseCloneable { if (object is ParseObject) { uniqueObjects.remove(object); } - for (ParseFile file in uniqueFiles) { + for (ParseFileBase file in uniqueFiles) { final ParseResponse response = await file.save(); if (!response.success) { return response; @@ -224,7 +224,7 @@ class ParseObject extends ParseBase implements ParseCloneable { bool _canbeSerialized(List aftersaving, {dynamic value}) { if (value != null) { if (value is ParseObject) { - if (value is ParseFile) { + if (value is ParseFileBase) { if (!value.saved && !aftersaving.contains(value)) { return false; } @@ -254,7 +254,7 @@ class ParseObject extends ParseBase implements ParseCloneable { bool _collectionDirtyChildren( dynamic object, Set uniqueObjects, - Set uniqueFiles, + Set uniqueFiles, Set seen, Set seenNew) { if (object is List) { @@ -273,7 +273,7 @@ class ParseObject extends ParseBase implements ParseCloneable { } } else if (object is ParseACL) { // TODO(yulingtianxia): handle ACL - } else if (object is ParseFile) { + } else if (object is ParseFileBase) { if (!object.saved) { uniqueFiles.add(object); } diff --git a/lib/src/utils/parse_decoder.dart b/lib/src/utils/parse_decoder.dart index f6628dc88..f2d0d929e 100644 --- a/lib/src/utils/parse_decoder.dart +++ b/lib/src/utils/parse_decoder.dart @@ -62,7 +62,8 @@ dynamic parseDecode(dynamic value) { final String className = map['className']; return ParseCoreData.instance.createObject(className).fromJson(map); case 'File': - return ParseFile(null, url: map['url'], name: map['name']) + return ParseCoreData.instance + .createFile(url: map['url'], name: map['name']) .fromJson(map); case 'GeoPoint': final num latitude = map['latitude'] ?? 0.0; @@ -70,7 +71,7 @@ dynamic parseDecode(dynamic value) { return ParseGeoPoint( latitude: latitude.toDouble(), longitude: longitude.toDouble()); case 'Relation': - // ignore: always_specify_types + // ignore: always_specify_types return ParseRelation().fromJson(map); } } diff --git a/lib/src/utils/parse_encoder.dart b/lib/src/utils/parse_encoder.dart index ffd12e417..50e8e7f82 100644 --- a/lib/src/utils/parse_encoder.dart +++ b/lib/src/utils/parse_encoder.dart @@ -36,7 +36,7 @@ dynamic parseEncode(dynamic value, {bool full}) { return value; } - if (value is ParseFile) { + if (value is ParseFileBase) { return value; } From 2c0b21e40de0b3b8b5996478a49c852bdc42b43d Mon Sep 17 00:00:00 2001 From: maaeps Date: Sun, 9 Aug 2020 15:40:55 +0200 Subject: [PATCH 3/3] Update README.md --- README.md | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e9e5b27e0..c57a1d8a8 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,8 @@ ParseCoreData().registerUserSubClass((username, password, emailAddress, {client, ``` Providing a `ParseObject` as described above should still work, even if you have registered a different `SubClass`. +For custom file classes have a lock at [here](#File). + ## Add new values to objects To add a variable to an object call and retrieve it, call @@ -809,11 +811,44 @@ QueryBuilder query = ..whereRelatedTo('fruits', 'DietPlan', DietPlan.objectId); ``` +## File +There are three different file classes in this SDK: +- `ParseFileBase` is and abstract class and is the foundation of every file class that can be handled by this SDK. +- `ParseFile` (former the only file class in the SDK) extends ParseFileBase and is by default used as the file class on every platform but web. +This class uses a `File` from `dart:io` for storing the raw file. +- `ParseWebFile` is the equivalent to ParseFile used at Flutter Web. +This class uses an `Uint8List` for storing the raw file. + +These classes are used by default to represent files, but you can also build your own class extending ParseFileBase and provide a custom `ParseFileConstructor` similar to the `SubClasses`. + +Have a look at the example application for a small (non web) example. + + +```dart +//A short example +Widget buildImage(ParseFileBase image){ + return FutureBuilder( + future: image.download(), + builder: (BuildContext context, + AsyncSnapshot snapshot) { + if (snapshot.hasData) { + if (kIsWeb) { + return Image.memory((snapshot.data as ParseWebFile).file); + } else { + return Image.file((snapshot.data as ParseFile).file); + } + } else { + return CircularProgressIndicator(); + } + }, + ); +} +``` + ## 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