Skip to content

feat: Add saveEventually and deleteEventually in ParseObject #911

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 34 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
6fbd06c
add saveEventually and deleteEventually
mbfakourii May 19, 2023
6ea7fc3
Merge branch 'master' into add_eventually
mbfakourii May 19, 2023
c9b68a9
Merge branch 'master' into add_eventually
mbfakourii May 20, 2023
46a9a9e
Merge branch 'master' into add_eventually
mtrezza May 20, 2023
354054b
Merge branch 'master' into add_eventually
mbfakourii May 20, 2023
e1a5948
Merge branch 'master' into add_eventually
mbfakourii May 23, 2023
cde971b
Create CONTRIBUTING.md
mbfakourii May 25, 2023
4e7ba14
Merge pull request #1 from mbfakourii/mbfakourii-patch-1
mbfakourii May 25, 2023
835a1db
just some styling
mtrezza May 25, 2023
2b6997f
Merge branch 'parse-community:master' into master
mbfakourii May 25, 2023
45928d4
Merge branch 'parse-community:master' into master
mbfakourii May 26, 2023
39168d3
Merge branch 'master' into add_eventually
mbfakourii May 26, 2023
f86d134
Merge branch 'parse-community:master' into master
mbfakourii May 28, 2023
79f9d4b
Merge branch 'parse-community:master' into master
mbfakourii May 29, 2023
54cae60
Merge branch 'master' into add_eventually
mbfakourii May 29, 2023
e3e2b15
Merge branch 'parse-community:master' into master
mbfakourii May 29, 2023
b27493f
Merge branch 'master' into add_eventually
mbfakourii May 29, 2023
b9c7e7a
Merge branch 'master' into add_eventually
mbfakourii May 29, 2023
1b96d48
Merge branch 'parse-community:master' into master
mbfakourii Jun 8, 2023
2ac7344
Merge branch 'parse-community:master' into master
mbfakourii Jun 20, 2023
9c77278
Merge branch 'parse-community:master' into master
mbfakourii Jul 3, 2023
99c081b
Merge branch 'master' into add_eventually
mbfakourii Jul 19, 2023
01cd124
Putting `submitEventually` in places to submit
mbfakourii Jul 19, 2023
69bffd2
Merge branch 'parse-community:master' into master
mbfakourii Jul 30, 2023
22971e5
Merge branch 'master' into add_eventually
mbfakourii Aug 9, 2023
f9c00d9
Merge branch 'parse-community:master' into master
mbfakourii Sep 25, 2023
b46a1b9
feat: add test
mbfakourii Oct 3, 2023
1ca99d5
fix: remove string and user response.
mbfakourii Oct 4, 2023
d98dc40
fix: remove string and user consts.
mbfakourii Oct 4, 2023
287845c
refactor: bump version and add entries in CHANGELOG.md.
mbfakourii Oct 9, 2023
9a6c089
Merge branch 'master' into add_eventually
mtrezza Oct 16, 2023
10ae85e
Merge branch 'parse-community:master' into master
mbfakourii Oct 17, 2023
638440b
Merge branch 'master' into add_eventually
mbfakourii Oct 17, 2023
36e8d56
Update packages/dart/CHANGELOG.md
mtrezza Oct 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions packages/dart/lib/parse_server_sdk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,62 +27,116 @@ export 'src/network/parse_dio_client.dart';
export 'src/network/parse_http_client.dart';

part 'src/base/parse_constants.dart';

part 'src/data/parse_core_data.dart';

part 'src/data/parse_subclass_handler.dart';

part 'src/enums/parse_enum_api_rq.dart';

part 'src/network/options.dart';

part 'src/network/parse_client.dart';

part 'src/network/parse_connectivity.dart';

part 'src/network/parse_live_query.dart';

part 'src/network/parse_query.dart';

part 'src/objects/parse_acl.dart';

part 'src/objects/parse_array.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_number.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_object.dart';

part 'src/objects/parse_exception.dart';

part 'src/objects/parse_operation/parse_add_operation.dart';

part 'src/objects/parse_operation/parse_add_relation_operation.dart';

part 'src/objects/parse_operation/parse_add_unique_operation.dart';

part 'src/objects/parse_operation/parse_increment_operation.dart';

part 'src/objects/parse_operation/parse_operation.dart';

part 'src/objects/parse_operation/parse_remove_operation.dart';

part 'src/objects/parse_operation/parse_remove_relation_operation.dart';

part 'src/objects/parse_relation.dart';

part 'src/objects/parse_response.dart';

part 'src/objects/parse_save_state_aware_child.dart';

part 'src/objects/parse_session.dart';

part 'src/objects/parse_user.dart';

part 'src/objects/response/parse_error_response.dart';

part 'src/objects/response/parse_exception_response.dart';

part 'src/objects/response/parse_response_builder.dart';

part 'src/objects/response/parse_response_utils.dart';

part 'src/objects/response/parse_success_no_results.dart';

part 'src/storage/core_store.dart';

part 'src/storage/core_store_memory.dart';

part 'src/storage/core_store_sem_impl.dart';

part 'src/storage/xxtea_codec.dart';

part 'src/utils/parse_date_format.dart';

part 'src/utils/parse_decoder.dart';

part 'src/utils/parse_encoder.dart';

part 'src/utils/parse_live_list.dart';

part 'src/utils/parse_logger.dart';

part 'src/utils/parse_login_helpers.dart';

part 'src/utils/parse_utils.dart';

part 'src/utils/valuable.dart';

class Parse {
bool _hasBeenInitialized = false;

static bool objectsExistForEventually = false;

/// To initialize Parse Server in your application
///
/// This should be initialized in MyApp() creation
Expand Down Expand Up @@ -148,6 +202,12 @@ class Parse {

_hasBeenInitialized = true;

objectsExistForEventually = await checkObjectsExistForEventually();

if (objectsExistForEventually) {
ParseObject.submitEventually();
}

return this;
}

Expand Down
2 changes: 2 additions & 0 deletions packages/dart/lib/src/base/parse_constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ const String keyParamSessionToken = 'sessionToken';
// Storage
const String keyParseStoreBase = 'flutter_parse_sdk_';
const String keyParseStoreUser = '${keyParseStoreBase}user';
const String keyParseStoreObjects = '${keyParseStoreBase}objects';
const String keyParseStoreDeletes = '${keyParseStoreBase}deletes';
const String keyParseStoreInstallation = '${keyParseStoreBase}installation';

// Installation
Expand Down
2 changes: 2 additions & 0 deletions packages/dart/lib/src/network/parse_dio_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ class _ParseDioClient with dio.DioMixin implements dio.Dio {
_logCUrl(options, data, path);
}

checkForSubmitEventually();

return super.request(
path,
data: data,
Expand Down
2 changes: 2 additions & 0 deletions packages/dart/lib/src/network/parse_http_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ class _ParseHTTPClient extends http.BaseClient {
_logCUrl(request);
}

checkForSubmitEventually();

return _client.send(request);
}

Expand Down
165 changes: 160 additions & 5 deletions packages/dart/lib/src/objects/parse_object.dart
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,25 @@ class ParseObject extends ParseBase implements ParseCloneable {
}
}

Future<ParseResponse> saveEventually() async {
// save object
final ParseResponse response = await save();

if (response.success) {
// return success response
return response;
} else {
// if have network connection error
if ((response.error?.message ?? "").contains("NetworkError")) {
// save this object in CoreStore
await _addThisObjectToParseCoreDataList(keyParseStoreObjects);
} else {
return response;
}
}
return response;
}

/// Saves the current object online.
///
/// If the object not saved yet, this will create it. Otherwise,
Expand Down Expand Up @@ -186,7 +205,7 @@ class ParseObject extends ParseBase implements ParseCloneable {
///
/// Prefer using [save] over [update] and [create]
Future<ParseResponse> save() async {
final ParseResponse childrenResponse = await _saveChildren(this);
final ParseResponse childrenResponse = await _saveChildren(this, _client);
if (childrenResponse.success) {
ParseResponse? response;
if (objectId == null) {
Expand All @@ -207,7 +226,7 @@ class ParseObject extends ParseBase implements ParseCloneable {
return childrenResponse;
}

Future<ParseResponse> _saveChildren(dynamic object) async {
static Future<ParseResponse> _saveChildren(dynamic object, client) async {
final Set<ParseObject> uniqueObjects = <ParseObject>{};
final Set<ParseFileBase> uniqueFiles = <ParseFileBase>{};
if (!_collectionDirtyChildren(
Expand Down Expand Up @@ -253,7 +272,7 @@ class ParseObject extends ParseBase implements ParseCloneable {

// TODO(yulingtianxia): lazy User
/* Batch requests have currently a limit of 50 packaged requests per single request
This splitting will split the overall array into segments of upto 50 requests
This splitting will split the overall array into segments of up to 50 requests
and execute them concurrently with a wrapper task for all of them. */
final List<List<ParseObject>> chunks = <List<ParseObject>>[];
for (int i = 0; i < current.length; i += 50) {
Expand All @@ -271,7 +290,7 @@ class ParseObject extends ParseBase implements ParseCloneable {
final ParseResponse response = await batchRequest(
requests,
chunk,
client: _client,
client: client,
);
totalResponse.success &= response.success;

Expand Down Expand Up @@ -371,7 +390,7 @@ class ParseObject extends ParseBase implements ParseCloneable {
return true;
}

bool _collectionDirtyChildren(
static bool _collectionDirtyChildren(
dynamic object,
Set<ParseObject> uniqueObjects,
Set<ParseFileBase> uniqueFiles,
Expand Down Expand Up @@ -625,6 +644,32 @@ class ParseObject extends ParseBase implements ParseCloneable {
}
}

Future<ParseResponse> deleteEventually() async {
// save object
final ParseResponse response = await delete();

if (response.success) {
// return success response
return response;
} else {
// if have network connection error
if ((response.error?.message ?? "").contains("NetworkError")) {
// save this object in CoreStore
await _addThisObjectToParseCoreDataList(keyParseStoreDeletes);
} else {
return response;
}
}
return response;
}

Future<void> _addThisObjectToParseCoreDataList(String key) async {
final CoreStore coreStore = ParseCoreData().getStore();
List<String> list = await coreStore.getStringList(key) ?? [];
list.add(json.encode(toJson(full: true)));
await coreStore.setStringList(key, list);
}

/// Deletes the current object locally and online
Future<ParseResponse> delete<T extends ParseObject>({
String? id,
Expand Down Expand Up @@ -677,4 +722,114 @@ class ParseObject extends ParseBase implements ParseCloneable {
return this;
}
}

static Future<void> submitEventually(
{ParseClient? client, bool? autoSendSessionId}) async {
await submitSaveEventually(
client: client, autoSendSessionId: autoSendSessionId);
await submitDeleteEventually(
client: client, autoSendSessionId: autoSendSessionId);

Parse.objectsExistForEventually = await checkObjectsExistForEventually();
}

static Future<ParseResponse?> submitSaveEventually(
{ParseClient? client, bool? autoSendSessionId}) async {
// get client
ParseClient localClient = client ??
ParseCoreData().clientCreator(
sendSessionId:
autoSendSessionId ?? ParseCoreData().autoSendSessionId,
securityContext: ParseCoreData().securityContext);

// preparation ParseCoreData
final CoreStore coreStore = ParseCoreData().getStore();

// save
// get json parse saved objects
List<String>? listSaves =
await coreStore.getStringList(keyParseStoreObjects);

if (listSaves != null) {
List<ParseObject> parseObjectList = [];
for (var element in listSaves) {
// decode json
dynamic object = json.decode(element);
parseObjectList
.add(ParseObject(object[keyVarClassName]).fromJson(object));
}

// send parseObjects to server
ParseResponse response =
await ParseObject._saveChildren(parseObjectList, localClient);

// if success clear all objects
if (response.success) {
coreStore.setStringList(keyParseStoreObjects, []);
}

return response;
}

return null;
}

static Future<ParseResponse?> submitDeleteEventually(
{ParseClient? client, bool? autoSendSessionId}) async {
// preparation ParseCoreData
final CoreStore coreStore = ParseCoreData().getStore();

// delete
// get json parse saved objects
List<String>? listDeletes =
await coreStore.getStringList(keyParseStoreDeletes);

if (listDeletes != null) {
int firstLength = listDeletes.length;
List<String> elementsToRemove = [];
for (var element in listDeletes) {
// decode json
dynamic object = json.decode(element);

// crate parse object
ParseObject parseObject = ParseObject(object[keyVarClassName],
client: client, autoSendSessionId: autoSendSessionId)
.fromJson(object);

// delete parse object
ParseResponse deleteResponse = await parseObject.delete();

if (deleteResponse.success) {
// remove from list Deletes
elementsToRemove.add(element);
}
}

// Remove the elements after the loop
for (var elementToRemove in elementsToRemove) {
listDeletes.remove(elementToRemove);
}

// set new list deletes in coreStore
coreStore.setStringList(keyParseStoreDeletes, listDeletes);

bool success = true;
int statusCode = 200;

if (listDeletes.length == firstLength) {
// Nothing has been deleted
success = false;
statusCode = -1;
}

final ParseResponse response = ParseResponse()
..success = success
..results = <dynamic>[]
..statusCode = statusCode;

return response;
}

return null;
}
}
Loading