Skip to content
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

Remove Email/get when cleaning mailbox #3383

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions contact/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -660,10 +660,10 @@ packages:
description:
path: "."
ref: main
resolved-ref: bb4ffba9e3aba2cbb60c1173663abf975b521199
resolved-ref: "00a9760ed2533e26bdb20b57189411582b5dc316"
url: "https://github.com/linagora/jmap-dart-client.git"
source: git
version: "0.3.0"
version: "0.3.1"
js:
dependency: transitive
description:
Expand Down
4 changes: 2 additions & 2 deletions email_recovery/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -296,10 +296,10 @@ packages:
description:
path: "."
ref: main
resolved-ref: bb4ffba9e3aba2cbb60c1173663abf975b521199
resolved-ref: "00a9760ed2533e26bdb20b57189411582b5dc316"
url: "https://github.com/linagora/jmap-dart-client.git"
source: git
version: "0.3.0"
version: "0.3.1"
js:
dependency: transitive
description:
Expand Down
4 changes: 2 additions & 2 deletions fcm/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -296,10 +296,10 @@ packages:
description:
path: "."
ref: main
resolved-ref: bb4ffba9e3aba2cbb60c1173663abf975b521199
resolved-ref: "00a9760ed2533e26bdb20b57189411582b5dc316"
url: "https://github.com/linagora/jmap-dart-client.git"
source: git
version: "0.3.0"
version: "0.3.1"
js:
dependency: transitive
description:
Expand Down
4 changes: 2 additions & 2 deletions forward/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -296,10 +296,10 @@ packages:
description:
path: "."
ref: main
resolved-ref: bb4ffba9e3aba2cbb60c1173663abf975b521199
resolved-ref: "00a9760ed2533e26bdb20b57189411582b5dc316"
url: "https://github.com/linagora/jmap-dart-client.git"
source: git
version: "0.3.0"
version: "0.3.1"
js:
dependency: transitive
description:
Expand Down
2 changes: 1 addition & 1 deletion lib/features/thread/data/datasource/thread_datasource.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ abstract class ThreadDataSource {

Future<void> update(AccountId accountId, UserName userName, {List<Email>? updated, List<Email>? created, List<EmailId>? destroyed});

Future<List<EmailId>> emptyMailboxFolder(
Future<bool> emptyMailboxFolder(
Session session,
AccountId accountId,
MailboxId mailboxId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ class LocalThreadDataSourceImpl extends ThreadDataSource {
}

@override
Future<List<EmailId>> emptyMailboxFolder(
Future<bool> emptyMailboxFolder(
Session session,
AccountId accountId,
MailboxId mailboxId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class ThreadDataSourceImpl extends ThreadDataSource {
}

@override
Future<List<EmailId>> emptyMailboxFolder(
Future<bool> emptyMailboxFolder(
Session session,
AccountId accountId,
MailboxId mailboxId
Expand Down
100 changes: 97 additions & 3 deletions lib/features/thread/data/network/thread_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:async';
import 'package:core/utils/app_logger.dart';
import 'package:jmap_dart_client/http/http_client.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/error/set_error.dart';
import 'package:jmap_dart_client/jmap/core/filter/filter.dart';
import 'package:jmap_dart_client/jmap/core/properties/properties.dart';
import 'package:jmap_dart_client/jmap/core/request/reference_path.dart';
Expand All @@ -20,17 +21,22 @@ import 'package:jmap_dart_client/jmap/mail/email/get/get_email_method.dart';
import 'package:jmap_dart_client/jmap/mail/email/get/get_email_response.dart';
import 'package:jmap_dart_client/jmap/mail/email/query/query_email_method.dart';
import 'package:jmap_dart_client/jmap/mail/email/query/query_email_response.dart';
import 'package:model/extensions/list_email_extension.dart';
import 'package:tmail_ui_user/features/thread/data/extensions/list_email_extension.dart';
import 'package:jmap_dart_client/jmap/mail/email/search_snippet/search_snippet.dart';
import 'package:jmap_dart_client/jmap/mail/email/search_snippet/search_snippet_get_method.dart';
import 'package:jmap_dart_client/jmap/mail/email/search_snippet/search_snippet_get_response.dart';
import 'package:jmap_dart_client/jmap/mail/email/set/set_email_method.dart';
import 'package:jmap_dart_client/jmap/mail/email/set/set_email_response.dart';
import 'package:model/error_type_handler/set_method_error_handler_mixin.dart';
import 'package:model/extensions/list_email_extension.dart';
import 'package:tmail_ui_user/features/base/mixin/handle_error_mixin.dart';
import 'package:tmail_ui_user/features/thread/data/extensions/list_email_extension.dart';
import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart';
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
import 'package:tmail_ui_user/features/thread/domain/model/empty_mailbox_response.dart';
import 'package:tmail_ui_user/features/thread/domain/model/search_emails_response.dart';
import 'package:tmail_ui_user/main/error/capability_validator.dart';

class ThreadAPI {
class ThreadAPI with HandleSetErrorMixin {

final HttpClient httpClient;

Expand Down Expand Up @@ -303,4 +309,92 @@ class ThreadAPI {

return resultList!.list.first;
}

Future<EmptyMailboxResponse> deleteEmailsBaseOnQuery(
Session session,
AccountId accountId,
{
UnsignedInt? limit,
int? position,
Set<Comparator>? sort,
Filter? filter,
}
) async {
final processingInvocation = ProcessingInvocation();

final jmapRequestBuilder = JmapRequestBuilder(httpClient, processingInvocation);

final queryEmailMethod = QueryEmailMethod(accountId);

if (limit != null) queryEmailMethod.addLimit(limit);

if (position != null) queryEmailMethod.addPosition(position);

if (sort != null) queryEmailMethod.addSorts(sort);

if (filter != null) queryEmailMethod.addFilters(filter);

final queryEmailInvocation = jmapRequestBuilder.invocation(queryEmailMethod);

final setEmailMethod = SetEmailMethod(accountId);

setEmailMethod.addReferenceDestroy(processingInvocation.createResultReference(
queryEmailInvocation.methodCallId,
ReferencePath.idsPath
));

final setEmailInvocation = jmapRequestBuilder.invocation(setEmailMethod);

final capabilities = setEmailMethod.requiredCapabilities;

final result = await (jmapRequestBuilder
..usings(capabilities))
.build()
.execute();

final queryResponse = result.parse<QueryEmailResponse>(
queryEmailInvocation.methodCallId,
QueryEmailResponse.deserialize
);

final setEmailResponse = result.parse<SetEmailResponse>(
setEmailInvocation.methodCallId,
SetEmailResponse.deserialize
);

final destroyed = setEmailResponse?.destroyed
?.map((id) => EmailId(id));

final queriedCount = queryResponse?.ids.length ?? 0;
final destroyedCount = destroyed?.length ?? 0;
log('ThreadAPI::deleteEmailsBaseOnQuery(): QUERIED_COUNT = $queriedCount, DESTROYED_COUNT = $destroyedCount');

if (destroyedCount < queriedCount) {
final notFoundErrorCounter = _countingNotFoundInNotDestroy(setEmailResponse);
final isSuccess = destroyedCount + notFoundErrorCounter >= queriedCount;
tddang-linagora marked this conversation as resolved.
Show resolved Hide resolved
return EmptyMailboxResponse(isSuccess, destroyedCount, notFoundErrorCounter);
}
return EmptyMailboxResponse(true, destroyedCount, 0);
}

int _countingNotFoundInNotDestroy(SetEmailResponse? setEmailResponse) {
int notFoundCount = 0;
final notDestroyedHandlers = <SetMethodErrorHandler>{
(entry) {
if (entry.value.type == SetError.notFound) {
notFoundCount++;
return true;
}
return false;
}
};

handleSetErrors(
notDestroyedError: setEmailResponse?.notDestroyed,
notDestroyedHandlers: notDestroyedHandlers
);

log('ThreadAPI::_countingNotFoundInNotDestroy: NOT_FOUND_ERRORS_COUNT = $notFoundCount');
return notFoundCount;
}
}
87 changes: 20 additions & 67 deletions lib/features/thread/data/network/thread_isolate_worker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,17 @@ import 'dart:async';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/platform_info.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/properties/properties.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:jmap_dart_client/jmap/core/sort/comparator.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_comparator.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_comparator_property.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/email/email_property.dart';
import 'package:model/extensions/list_email_extension.dart';
import 'package:tmail_ui_user/features/base/isolate/background_isolate_binary_messenger/background_isolate_binary_messenger.dart';
import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart';
import 'package:tmail_ui_user/features/email/data/network/email_api.dart';
import 'package:tmail_ui_user/features/thread/data/model/empty_mailbox_folder_arguments.dart';
import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart';
import 'package:tmail_ui_user/features/thread/domain/exceptions/thread_exceptions.dart';
import 'package:tmail_ui_user/main/exceptions/isolate_exception.dart';
import 'package:worker_manager/worker_manager.dart';

Expand All @@ -29,7 +24,7 @@ class ThreadIsolateWorker {

ThreadIsolateWorker(this._threadAPI, this._emailAPI, this._isolateExecutor);

Future<List<EmailId>> emptyMailboxFolder(
Future<bool> emptyMailboxFolder(
Session session,
AccountId accountId,
MailboxId mailboxId,
Expand All @@ -54,15 +49,11 @@ class ThreadIsolateWorker {
fun1: _emptyMailboxFolderAction
);

if (result.isEmpty) {
throw NotFoundEmailsDeletedException();
} else {
return result;
}
return result;
}
}

static Future<List<EmailId>> _emptyMailboxFolderAction(
static Future<bool> _emptyMailboxFolderAction(
EmptyMailboxFolderArguments args,
TypeSendPort sendPort
) async {
Expand All @@ -71,91 +62,53 @@ class ThreadIsolateWorker {
BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken);
await HiveCacheConfig.instance.setUp();

List<EmailId> emailListCompleted = List.empty(growable: true);

bool batchResult = true;
var hasEmails = true;
Email? lastEmail;

while (hasEmails) {
final emailsResponse = await args.threadAPI.getAllEmail(
final emptyMailboxResponse = await args.threadAPI.deleteEmailsBaseOnQuery(
args.session,
args.accountId,
sort: <Comparator>{}..add(
EmailComparator(EmailComparatorProperty.receivedAt)
..setIsAscending(false)),
filter: EmailFilterCondition(inMailbox: args.mailboxId, before: lastEmail?.receivedAt),
properties: Properties({EmailProperty.id}));

var newEmailList = emailsResponse.emailList ?? <Email>[];
if (lastEmail != null) {
newEmailList = newEmailList.where((email) => email.id != lastEmail!.id).toList();
}

log('ThreadIsolateWorker::_emptyMailboxFolderAction(): ${newEmailList.length}');

if (newEmailList.isNotEmpty) {
lastEmail = newEmailList.last;
hasEmails = true;
final listEmailIdDeleted = await args.emailAPI.deleteMultipleEmailsPermanently(
args.session,
args.accountId,
newEmailList.listEmailIds);
emailListCompleted.addAll(listEmailIdDeleted);
} else {
hasEmails = false;
}
filter: EmailFilterCondition(inMailbox: args.mailboxId),
);
batchResult = !batchResult ? batchResult : emptyMailboxResponse.isSuccess;
hasEmails = emptyMailboxResponse.destroyedCount > 0;
}
log('ThreadIsolateWorker::_emptyMailboxFolderAction(): TOTAL_REMOVE: ${emailListCompleted.length}');
return emailListCompleted;
return batchResult;
} catch (e) {
logError('ThreadIsolateWorker::_emptyMailboxFolderAction(): ERROR: $e');
rethrow;
}
}

Future<List<EmailId>> _emptyMailboxFolderOnWeb(
Future<bool> _emptyMailboxFolderOnWeb(
Session session,
AccountId accountId,
MailboxId mailboxId,
) async {
List<EmailId> emailListCompleted = List.empty(growable: true);
bool batchResult = true;
try {
var hasEmails = true;
Email? lastEmail;

while (hasEmails) {
final emailsResponse = await _threadAPI.getAllEmail(
session,
final emptyMailboxResponse = await _threadAPI.deleteEmailsBaseOnQuery(
session,
accountId,
sort: <Comparator>{}..add(
EmailComparator(EmailComparatorProperty.receivedAt)
..setIsAscending(false)),
filter: EmailFilterCondition(inMailbox: mailboxId, before: lastEmail?.receivedAt),
properties: Properties({EmailProperty.id}));

var newEmailList = emailsResponse.emailList ?? <Email>[];
if (lastEmail != null) {
newEmailList = newEmailList.where((email) => email.id != lastEmail!.id).toList();
}

log('ThreadIsolateWorker::_emptyMailboxFolderOnWeb(): ${newEmailList.length}');

if (newEmailList.isNotEmpty) {
lastEmail = newEmailList.last;
hasEmails = true;
final listEmailIdDeleted = await _emailAPI.deleteMultipleEmailsPermanently(
session,
accountId,
newEmailList.listEmailIds);
emailListCompleted.addAll(listEmailIdDeleted);
} else {
hasEmails = false;
}
filter: EmailFilterCondition(inMailbox: mailboxId),
);
batchResult = !batchResult ? batchResult : emptyMailboxResponse.isSuccess;
hasEmails = emptyMailboxResponse.destroyedCount > 0;
}
} catch (e) {
batchResult = false;
log('ThreadIsolateWorker::_emptyMailboxFolderOnWeb(): ERROR: $e');
}
log('ThreadIsolateWorker::_emptyMailboxFolderOnWeb(): TOTAL_REMOVE: ${emailListCompleted.length}');
return emailListCompleted;
return batchResult;
}
}
Loading
Loading