TF-3370 Handle SetError when make email action (Mark as read/star/move/delete)

This commit is contained in:
dab246
2025-01-02 13:58:30 +07:00
committed by Dat H. Pham
parent 47963d162b
commit 0f94e52d35
17 changed files with 800 additions and 181 deletions
@@ -9,6 +9,8 @@ import 'package:dio/dio.dart';
import 'package:email_recovery/email_recovery/email_recovery_action.dart';
import 'package:email_recovery/email_recovery/email_recovery_action_id.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/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/user_name.dart';
@@ -43,7 +45,10 @@ abstract class EmailDataSource {
}
);
Future<List<EmailId>> markAsRead(
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> markAsRead(
Session session,
AccountId accountId,
List<EmailId> emailIds,
@@ -75,9 +80,19 @@ abstract class EmailDataSource {
{CancelToken? cancelToken}
);
Future<List<EmailId>> moveToMailbox(Session session, AccountId accountId, MoveToMailboxRequest moveRequest);
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> moveToMailbox(
Session session,
AccountId accountId,
MoveToMailboxRequest moveRequest,
);
Future<List<EmailId>> markAsStar(
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> markAsStar(
Session session,
AccountId accountId,
List<EmailId> emailIds,
@@ -106,8 +121,14 @@ abstract class EmailDataSource {
{CancelToken? cancelToken}
);
Future<List<EmailId>> deleteMultipleEmailsPermanently(Session session, AccountId accountId, List<EmailId> emailIds);
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> deleteMultipleEmailsPermanently(
Session session,
AccountId accountId,
List<EmailId> emailIds,
);
Future<bool> deleteEmailPermanently(
Session session,
AccountId accountId,
@@ -10,6 +10,8 @@ import 'package:email_recovery/email_recovery/email_recovery_action.dart';
import 'package:email_recovery/email_recovery/email_recovery_action_id.dart';
import 'package:get/get.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/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/user_name.dart';
@@ -75,7 +77,10 @@ class EmailDataSourceImpl extends EmailDataSource {
}
@override
Future<List<EmailId>> markAsRead(
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> markAsRead(
Session session,
AccountId accountId,
List<EmailId> emailIds,
@@ -112,14 +117,24 @@ class EmailDataSourceImpl extends EmailDataSource {
}
@override
Future<List<EmailId>> moveToMailbox(Session session, AccountId accountId, MoveToMailboxRequest moveRequest) {
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> moveToMailbox(
Session session,
AccountId accountId,
MoveToMailboxRequest moveRequest,
) {
return Future.sync(() async {
return await emailAPI.moveToMailbox(session, accountId, moveRequest);
}).catchError(_exceptionThrower.throwException);
}
@override
Future<List<EmailId>> markAsStar(
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> markAsStar(
Session session,
AccountId accountId,
List<EmailId> emailIds,
@@ -211,9 +226,20 @@ class EmailDataSourceImpl extends EmailDataSource {
}
@override
Future<List<EmailId>> deleteMultipleEmailsPermanently(Session session, AccountId accountId, List<EmailId> emailIds) {
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> deleteMultipleEmailsPermanently(
Session session,
AccountId accountId,
List<EmailId> emailIds,
) {
return Future.sync(() async {
return await emailAPI.deleteMultipleEmailsPermanently(session, accountId, emailIds);
return await emailAPI.deleteMultipleEmailsPermanently(
session,
accountId,
emailIds,
);
}).catchError(_exceptionThrower.throwException);
}
@@ -12,6 +12,8 @@ import 'package:dio/dio.dart';
import 'package:email_recovery/email_recovery/email_recovery_action.dart';
import 'package:email_recovery/email_recovery/email_recovery_action_id.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/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/user_name.dart';
@@ -82,7 +84,14 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
}
@override
Future<List<EmailId>> deleteMultipleEmailsPermanently(Session session, AccountId accountId, List<EmailId> emailIds) {
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> deleteMultipleEmailsPermanently(
Session session,
AccountId accountId,
List<EmailId> emailIds,
) {
throw UnimplementedError();
}
@@ -120,7 +129,10 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
}
@override
Future<List<EmailId>> markAsRead(
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> markAsRead(
Session session,
AccountId accountId,
List<EmailId> emailIds,
@@ -130,12 +142,27 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
}
@override
Future<List<EmailId>> markAsStar(Session session, AccountId accountId, List<EmailId> emailIds, MarkStarAction markStarAction) {
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> markAsStar(
Session session,
AccountId accountId,
List<EmailId> emailIds,
MarkStarAction markStarAction,
) {
throw UnimplementedError();
}
@override
Future<List<EmailId>> moveToMailbox(Session session, AccountId accountId, MoveToMailboxRequest moveRequest) {
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> moveToMailbox(
Session session,
AccountId accountId,
MoveToMailboxRequest moveRequest,
) {
throw UnimplementedError();
}
+124 -89
View File
@@ -18,6 +18,7 @@ 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/capability/capability_identifier.dart';
import 'package:jmap_dart_client/jmap/core/capability/core_capability.dart';
import 'package:jmap_dart_client/jmap/core/error/set_error.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/core/patch_object.dart';
import 'package:jmap_dart_client/jmap/core/properties/properties.dart';
@@ -44,13 +45,13 @@ import 'package:model/account/account_request.dart';
import 'package:model/account/authentication_type.dart';
import 'package:model/download/download_task_id.dart';
import 'package:model/email/attachment.dart';
import 'package:model/email/email_property.dart';
import 'package:model/email/mark_star_action.dart';
import 'package:model/email/read_actions.dart';
import 'package:model/extensions/email_extension.dart';
import 'package:model/extensions/email_id_extensions.dart';
import 'package:model/extensions/keyword_identifier_extension.dart';
import 'package:model/extensions/list_email_id_extension.dart';
import 'package:model/extensions/list_id_extension.dart';
import 'package:model/extensions/mailbox_id_extension.dart';
import 'package:model/extensions/session_extension.dart';
import 'package:model/oidc/token_oidc.dart';
@@ -237,16 +238,21 @@ class EmailAPI with HandleSetErrorMixin {
}
}
Future<List<EmailId>> markAsRead(
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> markAsRead(
Session session,
AccountId accountId,
List<EmailId> emailIds,
ReadActions readActions,
) async {
final maxBatches = _getMaxObjectsInSetMethod(session, accountId);
final totalEmails = emails.length;
final maxObjects = _getMaxObjectsInSetMethod(session, accountId);
final totalEmails = emailIds.length;
final maxBatches = min(totalEmails, maxObjects);
final List<Email> updatedEmails = List.empty(growable: true);
final List<EmailId> updatedEmailIds = List.empty(growable: true);
final Map<Id, SetError> mapErrors = <Id, SetError>{};
for (int start = 0; start < totalEmails; start += maxBatches) {
int end = (start + maxBatches < totalEmails)
@@ -254,20 +260,16 @@ class EmailAPI with HandleSetErrorMixin {
: totalEmails;
log('EmailAPI::markAsRead:emails from ${start + 1} to $end');
final currentListEmails = emails.sublist(start, end);
final currentListEmailIds = emailIds.sublist(start, end);
final setEmailMethod = SetEmailMethod(accountId)
..addUpdates(currentListEmails.listEmailIds.generateMapUpdateObjectMarkAsRead(readActions));
final getEmailMethod = GetEmailMethod(accountId)
..addIds(emails.listEmailIds.toIds().toSet())
..addProperties(Properties({EmailProperty.keywords}));
..addUpdates(
currentListEmailIds.generateMapUpdateObjectMarkAsRead(readActions)
);
final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation());
requestBuilder.invocation(setEmailMethod);
final getEmailInvocation = requestBuilder.invocation(getEmailMethod);
final setEmailInvocation = requestBuilder.invocation(setEmailMethod);
final capabilities = setEmailMethod.requiredCapabilities
.toCapabilitiesSupportTeamMailboxes(session, accountId);
@@ -277,18 +279,19 @@ class EmailAPI with HandleSetErrorMixin {
.build()
.execute();
final getEmailResponse = response.parse<GetEmailResponse>(
getEmailInvocation.methodCallId,
GetEmailResponse.deserialize,
final setEmailResponse = response.parse<SetEmailResponse>(
setEmailInvocation.methodCallId,
SetEmailResponse.deserialize,
);
final listEmails = getEmailResponse?.list ?? [];
if (listEmails.isNotEmpty) {
updatedEmails.addAll(listEmails);
}
final listEmailIds = setEmailResponse?.updated?.keys.toEmailIds() ?? [];
final mapErrors = handleSetResponse([setEmailResponse]);
updatedEmailIds.addAll(listEmailIds);
mapErrors.addAll(mapErrors);
}
return updatedEmails;
return (emailIdsSuccess: updatedEmailIds, mapErrors: mapErrors);
}
Future<List<DownloadTaskId>> downloadAttachments(
@@ -397,47 +400,55 @@ class EmailAPI with HandleSetErrorMixin {
return bytesDownloaded;
}
Future<List<EmailId>> moveToMailbox(
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> moveToMailbox(
Session session,
AccountId accountId,
MoveToMailboxRequest moveRequest
) async {
final maxBatches = _getMaxObjectsInSetMethod(session, accountId);
final List<EmailId> listEmailIdResult = List.empty(growable: true);
final Map<Id, SetError> mapErrors = <Id, SetError>{};
final listMailboxIds = moveRequest.currentMailboxes.keys.toList();
for (int i = 0; i < listMailboxIds.length; i++) {
final currentMailboxId = listMailboxIds[i];
final listEmailIds = moveRequest.currentMailboxes[currentMailboxId]!;
log('EmailAPI::moveToMailbox:from mailbox ${currentMailboxId.asString} with ${listEmailIds.length} emails to mailbox ${moveRequest.destinationMailboxId.asString}');
final movedEmailIds = await _moveEmailsBetweenMailboxes(
final resultRecords = await _moveEmailsBetweenMailboxes(
session: session,
accountId: accountId,
listEmailIds: listEmailIds,
emailIds: listEmailIds,
currentMailboxId: currentMailboxId,
maxBatches: maxBatches,
destinationMailboxId: moveRequest.destinationMailboxId,
isMovingToSpam: moveRequest.isMovingToSpam,
);
listEmailIdResult.addAll(movedEmailIds);
listEmailIdResult.addAll(resultRecords.emailIdsSuccess);
mapErrors.addAll(resultRecords.mapErrors);
}
return listEmailIdResult;
return (emailIdsSuccess: listEmailIdResult, mapErrors: mapErrors);
}
Future<List<EmailId>> _moveEmailsBetweenMailboxes({
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> _moveEmailsBetweenMailboxes({
required Session session,
required AccountId accountId,
required List<EmailId> listEmailIds,
required List<EmailId> emailIds,
required MailboxId currentMailboxId,
required MailboxId destinationMailboxId,
required int maxBatches,
bool isMovingToSpam = false,
}) async {
final maxBatches = _getMaxObjectsInSetMethod(session, accountId);
final totalEmails = listEmailIds.length;
final maxObjects = _getMaxObjectsInSetMethod(session, accountId);
final totalEmails = emailIds.length;
final maxBatches = min(totalEmails, maxObjects);
final List<EmailId> updatedEmailIds = List.empty(growable: true);
final Map<Id, SetError> mapErrors = <Id, SetError>{};
for (int start = 0; start < totalEmails; start += maxBatches) {
int end = (start + maxBatches < totalEmails)
@@ -445,7 +456,7 @@ class EmailAPI with HandleSetErrorMixin {
: totalEmails;
log('EmailAPI::_moveEmailsBetweenMailboxes:emails from ${start + 1} to $end');
final currentEmailIds = listEmailIds.sublist(start, end);
final currentEmailIds = emailIds.sublist(start, end);
final moveProperties = isMovingToSpam
? currentEmailIds.generateMapUpdateObjectMoveToSpam(
@@ -477,13 +488,14 @@ class EmailAPI with HandleSetErrorMixin {
SetEmailResponse.deserialize,
);
final listIdsUpdated = setEmailResponse?.updated?.keys ?? [];
if (listIdsUpdated.isNotEmpty == true) {
final listEmailIdsUpdated = listIdsUpdated.map((e) => EmailId(e)).toList();
updatedEmailIds.addAll(listEmailIdsUpdated);
}
final listEmailIds = setEmailResponse?.updated?.keys.toEmailIds() ?? [];
final mapErrors = handleSetResponse([setEmailResponse]);
updatedEmailIds.addAll(listEmailIds);
mapErrors.addAll(mapErrors);
}
return updatedEmailIds;
return (emailIdsSuccess: updatedEmailIds, mapErrors: mapErrors);
}
int _getMaxObjectsInSetMethod(Session session, AccountId accountId) {
@@ -502,16 +514,21 @@ class EmailAPI with HandleSetErrorMixin {
return minOfMaxObjectsInSetMethod;
}
Future<List<EmailId>> markAsStar(
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> markAsStar(
Session session,
AccountId accountId,
List<EmailId> emailIds,
MarkStarAction markStarAction
) async {
final maxBatches = _getMaxObjectsInSetMethod(session, accountId);
final totalEmails = emails.length;
final maxObjects = _getMaxObjectsInSetMethod(session, accountId);
final totalEmails = emailIds.length;
final maxBatches = min(totalEmails, maxObjects);
final List<Email> updatedEmails = List.empty(growable: true);
final List<EmailId> updatedEmailIds = List.empty(growable: true);
final Map<Id, SetError> mapErrors = <Id, SetError>{};
for (int start = 0; start < totalEmails; start += maxBatches) {
int end = (start + maxBatches < totalEmails)
@@ -519,20 +536,16 @@ class EmailAPI with HandleSetErrorMixin {
: totalEmails;
log('EmailAPI::markAsStar:emails from ${start + 1} to $end');
final currentListEmails = emails.sublist(start, end);
final currentListEmailIds = emailIds.sublist(start, end);
final setEmailMethod = SetEmailMethod(accountId)
..addUpdates(currentListEmails.listEmailIds.generateMapUpdateObjectMarkAsStar(markStarAction));
final getEmailMethod = GetEmailMethod(accountId)
..addIds(emails.listEmailIds.toIds().toSet())
..addProperties(Properties({EmailProperty.keywords}));
..addUpdates(
currentListEmailIds.generateMapUpdateObjectMarkAsStar(markStarAction),
);
final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation());
requestBuilder.invocation(setEmailMethod);
final getEmailInvocation = requestBuilder.invocation(getEmailMethod);
final setEmailInvocation = requestBuilder.invocation(setEmailMethod);
final capabilities = setEmailMethod.requiredCapabilities
.toCapabilitiesSupportTeamMailboxes(session, accountId);
@@ -542,18 +555,19 @@ class EmailAPI with HandleSetErrorMixin {
.build()
.execute();
final getEmailResponse = response.parse<GetEmailResponse>(
getEmailInvocation.methodCallId,
GetEmailResponse.deserialize,
final setEmailResponse = response.parse<SetEmailResponse>(
setEmailInvocation.methodCallId,
SetEmailResponse.deserialize,
);
final listEmails = getEmailResponse?.list ?? [];
if (listEmails.isNotEmpty) {
updatedEmails.addAll(listEmails);
}
final listEmailIds = setEmailResponse?.updated?.keys.toEmailIds() ?? [];
final mapErrors = handleSetResponse([setEmailResponse]);
updatedEmailIds.addAll(listEmailIds);
mapErrors.addAll(mapErrors);
}
return updatedEmails;
return (emailIdsSuccess: updatedEmailIds, mapErrors: mapErrors);
}
Future<Email> saveEmailAsDrafts(
@@ -656,36 +670,56 @@ class EmailAPI with HandleSetErrorMixin {
return emailCreated;
}
Future<List<EmailId>> deleteMultipleEmailsPermanently(
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> deleteMultipleEmailsPermanently(
Session session,
AccountId accountId,
List<EmailId> emailIds
) async {
final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation());
final setEmailMethod = SetEmailMethod(accountId)
..addDestroy(emailIds.map((emailId) => emailId.id).toSet());
final maxObjects = _getMaxObjectsInSetMethod(session, accountId);
final totalEmails = emailIds.length;
final maxBatches = min(totalEmails, maxObjects);
final setEmailInvocation = requestBuilder.invocation(setEmailMethod);
final List<EmailId> destroyedEmailIds = List.empty(growable: true);
final Map<Id, SetError> mapErrors = <Id, SetError>{};
final capabilities = setEmailMethod.requiredCapabilities
.toCapabilitiesSupportTeamMailboxes(session, accountId);
for (int start = 0; start < totalEmails; start += maxBatches) {
int end = (start + maxBatches < totalEmails)
? start + maxBatches
: totalEmails;
log('EmailAPI::deleteMultipleEmailsPermanently:emails from ${start + 1} to $end');
final response = await (requestBuilder
..usings(capabilities))
.build()
.execute();
final currentListEmailIds = emailIds.sublist(start, end);
final setEmailResponse = response.parse<SetEmailResponse>(
final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation());
final setEmailMethod = SetEmailMethod(accountId)
..addDestroy(currentListEmailIds.toIds().toSet());
final setEmailInvocation = requestBuilder.invocation(setEmailMethod);
final capabilities = setEmailMethod.requiredCapabilities
.toCapabilitiesSupportTeamMailboxes(session, accountId);
final response = await (requestBuilder
..usings(capabilities))
.build()
.execute();
final setEmailResponse = response.parse<SetEmailResponse>(
setEmailInvocation.methodCallId,
SetEmailResponse.deserialize);
SetEmailResponse.deserialize,
);
final listIdResult = setEmailResponse?.destroyed;
final listEmailIds = setEmailResponse?.destroyed?.toEmailIds() ?? [];
final mapErrors = handleSetResponse([setEmailResponse]);
if (listIdResult != null) {
return listIdResult.map((id) => EmailId(id)).toList();
destroyedEmailIds.addAll(listEmailIds);
mapErrors.addAll(mapErrors);
}
return List.empty();
return (emailIdsSuccess: destroyedEmailIds, mapErrors: mapErrors);
}
Future<bool> deleteEmailPermanently(
@@ -698,19 +732,20 @@ class EmailAPI with HandleSetErrorMixin {
final setEmailMethod = SetEmailMethod(accountId)
..addDestroy({emailId.id});
final setEmailInvocation = requestBuilder.invocation(setEmailMethod);
final setEmailInvocation = requestBuilder.invocation(setEmailMethod);
final capabilities = setEmailMethod.requiredCapabilities
.toCapabilitiesSupportTeamMailboxes(session, accountId);
final capabilities = setEmailMethod.requiredCapabilities
.toCapabilitiesSupportTeamMailboxes(session, accountId);
final response = await (requestBuilder
..usings(capabilities))
.build()
.execute(cancelToken: cancelToken);
final setEmailResponse = response.parse<SetEmailResponse>(
final setEmailResponse = response.parse<SetEmailResponse>(
setEmailInvocation.methodCallId,
SetEmailResponse.deserialize);
SetEmailResponse.deserialize,
);
return setEmailResponse?.destroyed?.contains(emailId.id) == true;
}
@@ -757,10 +792,10 @@ class EmailAPI with HandleSetErrorMixin {
final capabilities = setEmailMethod.requiredCapabilities.toCapabilitiesSupportTeamMailboxes(session, accountId);
final response = await (requestBuilder
..usings(capabilities))
.build()
.execute();
final response = await (requestBuilder
..usings(capabilities))
.build()
.execute();
final setEmailResponse = response.parse<SetEmailResponse>(
setEmailInvocation.methodCallId,
@@ -11,6 +11,8 @@ import 'package:dio/dio.dart';
import 'package:email_recovery/email_recovery/email_recovery_action.dart';
import 'package:email_recovery/email_recovery/email_recovery_action_id.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/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/state.dart' as jmap;
@@ -83,7 +85,10 @@ class EmailRepositoryImpl extends EmailRepository {
}
@override
Future<List<EmailId>> markAsRead(
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> markAsRead(
Session session,
AccountId accountId,
List<EmailId> emailIds,
@@ -124,12 +129,26 @@ class EmailRepositoryImpl extends EmailRepository {
}
@override
Future<List<EmailId>> moveToMailbox(Session session, AccountId accountId, MoveToMailboxRequest moveRequest) {
return emailDataSource[DataSourceType.network]!.moveToMailbox(session, accountId, moveRequest);
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> moveToMailbox(
Session session,
AccountId accountId,
MoveToMailboxRequest moveRequest,
) {
return emailDataSource[DataSourceType.network]!.moveToMailbox(
session,
accountId,
moveRequest,
);
}
@override
Future<List<EmailId>> markAsStar(
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> markAsStar(
Session session,
AccountId accountId,
List<EmailId> emailIds,
@@ -228,8 +247,19 @@ class EmailRepositoryImpl extends EmailRepository {
}
@override
Future<List<EmailId>> deleteMultipleEmailsPermanently(Session session, AccountId accountId, List<EmailId> emailIds) {
return emailDataSource[DataSourceType.network]!.deleteMultipleEmailsPermanently(session, accountId, emailIds);
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> deleteMultipleEmailsPermanently(
Session session,
AccountId accountId,
List<EmailId> emailIds,
) {
return emailDataSource[DataSourceType.network]!.deleteMultipleEmailsPermanently(
session,
accountId,
emailIds,
);
}
@override
@@ -10,6 +10,8 @@ import 'package:dio/dio.dart';
import 'package:email_recovery/email_recovery/email_recovery_action.dart';
import 'package:email_recovery/email_recovery/email_recovery_action_id.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/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/state.dart' as jmap;
@@ -45,7 +47,10 @@ abstract class EmailRepository {
}
);
Future<List<EmailId>> markAsRead(
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> markAsRead(
Session session,
AccountId accountId,
List<EmailId> emailIds,
@@ -77,9 +82,19 @@ abstract class EmailRepository {
{CancelToken? cancelToken}
);
Future<List<EmailId>> moveToMailbox(Session session, AccountId accountId, MoveToMailboxRequest moveRequest);
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> moveToMailbox(
Session session,
AccountId accountId,
MoveToMailboxRequest moveRequest,
);
Future<List<EmailId>> markAsStar(
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> markAsStar(
Session session,
AccountId accountId,
List<EmailId> emailIds,
@@ -114,7 +129,14 @@ abstract class EmailRepository {
{CancelToken? cancelToken}
);
Future<List<EmailId>> deleteMultipleEmailsPermanently(Session session, AccountId accountId, List<EmailId> emailIds);
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
})> deleteMultipleEmailsPermanently(
Session session,
AccountId accountId,
List<EmailId> emailIds,
);
Future<bool> deleteEmailPermanently(
Session session,
@@ -16,10 +16,10 @@ class DeleteMultipleEmailsPermanentlyInteractor {
try {
yield Right<Failure, Success>(LoadingDeleteMultipleEmailsPermanentlyAll());
final listResult = await _emailRepository.deleteMultipleEmailsPermanently(session, accountId, emailIds);
if (listResult.length == emailIds.length) {
yield Right<Failure, Success>(DeleteMultipleEmailsPermanentlyAllSuccess(listResult));
} else if (listResult.isNotEmpty) {
yield Right<Failure, Success>(DeleteMultipleEmailsPermanentlyHasSomeEmailFailure(listResult));
if (listResult.emailIdsSuccess.length == emailIds.length) {
yield Right<Failure, Success>(DeleteMultipleEmailsPermanentlyAllSuccess(listResult.emailIdsSuccess));
} else if (listResult.emailIdsSuccess.isNotEmpty) {
yield Right<Failure, Success>(DeleteMultipleEmailsPermanentlyHasSomeEmailFailure(listResult.emailIdsSuccess));
} else {
yield Left<Failure, Success>(DeleteMultipleEmailsPermanentlyAllFailure());
}
@@ -29,11 +29,15 @@ class MarkAsEmailReadInteractor {
readAction,
);
yield Right(MarkAsEmailReadSuccess(
result.first,
readAction,
markReadAction,
));
if (result.emailIdsSuccess.isEmpty) {
yield Left(MarkAsEmailReadFailure(readAction));
} else {
yield Right(MarkAsEmailReadSuccess(
result.emailIdsSuccess.first,
readAction,
markReadAction,
));
}
} catch (e) {
yield Left(MarkAsEmailReadFailure(readAction, exception: e));
}
@@ -16,9 +16,9 @@ class MoveToMailboxInteractor {
try {
yield Right(LoadingMoveToMailbox());
final result = await _emailRepository.moveToMailbox(session, accountId, moveRequest);
if (result.isNotEmpty) {
if (result.emailIdsSuccess.isNotEmpty) {
yield Right(MoveToMailboxSuccess(
result.first,
result.emailIdsSuccess.first,
moveRequest.currentMailboxes.keys.first,
moveRequest.destinationMailboxId,
moveRequest.moveAction,
@@ -138,8 +138,8 @@ class MailboxIsolateWorker {
listEmailUnread.listEmailIds,
ReadActions.markAsRead);
log('MailboxIsolateWorker::_handleMarkAsMailboxRead(): MARK_READ: ${result.length}');
emailIdsCompleted.addAll(result);
log('MailboxIsolateWorker::_handleMarkAsMailboxRead(): MARK_READ: ${result.emailIdsSuccess.length}');
emailIdsCompleted.addAll(result.emailIdsSuccess);
sendPort.send(emailIdsCompleted);
}
}
@@ -206,8 +206,8 @@ class MailboxIsolateWorker {
listEmailUnread.listEmailIds,
ReadActions.markAsRead,
);
log('MailboxIsolateWorker::_handleMarkAsMailboxReadActionOnWeb(): MARK_READ: ${result.length}');
emailIdsCompleted.addAll(result);
log('MailboxIsolateWorker::_handleMarkAsMailboxReadActionOnWeb(): MARK_READ: ${result.emailIdsSuccess.length}');
emailIdsCompleted.addAll(result.emailIdsSuccess);
onProgressController.add(Right(UpdatingMarkAsMailboxReadState(
mailboxId: mailboxId,
@@ -100,7 +100,7 @@ class ThreadIsolateWorker {
..setIsAscending(false)),
filter: EmailFilterCondition(inMailbox: args.mailboxId, before: lastEmail?.receivedAt),
properties: Properties({
EmailProperty.id,
EmailProperty.id,
EmailProperty.receivedAt
}),
);
@@ -119,7 +119,7 @@ class ThreadIsolateWorker {
args.session,
args.accountId,
newEmailList.listEmailIds);
emailListCompleted.addAll(listEmailIdDeleted);
emailListCompleted.addAll(listEmailIdDeleted.emailIdsSuccess);
sendPort.send(emailListCompleted);
} else {
hasEmails = false;
@@ -154,7 +154,7 @@ class ThreadIsolateWorker {
..setIsAscending(false)),
filter: EmailFilterCondition(inMailbox: mailboxId, before: lastEmail?.receivedAt),
properties: Properties({
EmailProperty.id,
EmailProperty.id,
EmailProperty.receivedAt
}),
);
@@ -173,7 +173,7 @@ class ThreadIsolateWorker {
session,
accountId,
newEmailList.listEmailIds);
emailListCompleted.addAll(listEmailIdDeleted);
emailListCompleted.addAll(listEmailIdDeleted.emailIdsSuccess);
onProgressController.add(Right<Failure, Success>(EmptyingFolderState(
mailboxId, emailListCompleted.length, totalEmails
@@ -29,16 +29,16 @@ class MarkAsMultipleEmailReadInteractor {
readAction,
);
if (emailIds.length == result.length) {
if (emailIds.length == result.emailIdsSuccess.length) {
yield Right(MarkAsMultipleEmailReadAllSuccess(
result.length,
result.emailIdsSuccess.length,
readAction,
));
} else if (result.isEmpty) {
} else if (result.emailIdsSuccess.isEmpty) {
yield Left(MarkAsMultipleEmailReadAllFailure(readAction));
} else {
yield Right(MarkAsMultipleEmailReadHasSomeEmailFailure(
result.length,
result.emailIdsSuccess.length,
readAction,
));
}
@@ -24,16 +24,16 @@ class MarkAsStarMultipleEmailInteractor {
final result = await _emailRepository.markAsStar(session, accountId, emailIds, markStarAction);
if (emailIds.length == result.length) {
if (emailIds.length == result.emailIdsSuccess.length) {
yield Right(MarkAsStarMultipleEmailAllSuccess(
emailIds.length,
markStarAction,
));
} else if (result.isEmpty) {
} else if (result.emailIdsSuccess.isEmpty) {
yield Left(MarkAsStarMultipleEmailAllFailure(markStarAction));
} else {
yield Right(MarkAsStarMultipleEmailHasSomeEmailFailure(
result.length,
result.emailIdsSuccess.length,
markStarAction,
));
}
@@ -22,20 +22,20 @@ class MoveMultipleEmailToMailboxInteractor {
try {
yield Right(LoadingMoveMultipleEmailToMailboxAll());
final result = await _emailRepository.moveToMailbox(session, accountId, moveRequest);
if (moveRequest.totalEmails == result.length) {
if (moveRequest.totalEmails == result.emailIdsSuccess.length) {
yield Right(MoveMultipleEmailToMailboxAllSuccess(
result,
result.emailIdsSuccess,
moveRequest.currentMailboxes.keys.first,
moveRequest.destinationMailboxId,
moveRequest.moveAction,
moveRequest.emailActionType,
destinationPath: moveRequest.destinationPath,
));
} else if (result.isEmpty) {
} else if (result.emailIdsSuccess.isEmpty) {
yield Left(MoveMultipleEmailToMailboxAllFailure(moveRequest.moveAction, moveRequest.emailActionType));
} else {
yield Right(MoveMultipleEmailToMailboxHasSomeEmailFailure(
result,
result.emailIdsSuccess,
moveRequest.currentMailboxes.keys.first,
moveRequest.destinationMailboxId,
moveRequest.moveAction,
@@ -0,0 +1,6 @@
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
extension ListIdExtension on Iterable<Id> {
Iterable<EmailId> toEmailIds() => map((id) => EmailId(id));
}
+1
View File
@@ -60,6 +60,7 @@ export 'extensions/session_extension.dart';
export 'extensions/username_extension.dart';
export 'extensions/utc_date_extension.dart';
export 'extensions/contact_support_capability_extension.dart';
export 'extensions/list_id_extension.dart';
// Identity
export 'identity/identity_request_dto.dart';
export 'mailbox/expand_mode.dart';
@@ -1,5 +1,4 @@
import 'dart:math';
import 'package:collection/collection.dart';
import 'package:core/data/network/dio_client.dart';
import 'package:core/data/network/download/download_manager.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -60,7 +59,7 @@ void main() {
const defaultMaxObjectsInSet = 50;
const maxObjectsInSet = 50;
const totalEmails = 100;
final maxBatches = min(maxObjectsInSet, defaultMaxObjectsInSet);
final maxBatches = [maxObjectsInSet, defaultMaxObjectsInSet, totalEmails].min;
final countIterations = (totalEmails / maxBatches).ceil();
final aliceSession = SessionFixtures.getAliceSessionWithMaxObjectsInSet(maxObjectsInSet);
@@ -102,16 +101,16 @@ void main() {
]
});
final emails = List.generate(
final emailIds = List.generate(
totalEmails,
(index) => Email(id: EmailId(Id('email_$index'))),
(index) => EmailId(Id('email_$index')),
);
// Act
final result = await emailApi.markAsRead(
aliceSession,
AccountFixtures.aliceAccountId,
emails,
emailIds,
ReadActions.markAsRead,
);
@@ -121,7 +120,8 @@ void main() {
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).called(countIterations);
expect(result.length, totalEmails);
expect(result.emailIdsSuccess.length, totalEmails);
expect(result.mapErrors.isEmpty, isTrue);
});
test(
@@ -133,7 +133,7 @@ void main() {
const defaultMaxObjectsInSet = 50;
const maxObjectsInSet = 200;
const totalEmails = 100;
final maxBatches = min(maxObjectsInSet, defaultMaxObjectsInSet);
final maxBatches = [maxObjectsInSet, defaultMaxObjectsInSet, totalEmails].min;
final countIterations = (totalEmails / maxBatches).ceil();
final aliceSession = SessionFixtures.getAliceSessionWithMaxObjectsInSet(maxObjectsInSet);
@@ -175,16 +175,16 @@ void main() {
]
});
final emails = List.generate(
final emailIds = List.generate(
totalEmails,
(index) => Email(id: EmailId(Id('email_$index'))),
(index) => EmailId(Id('email_$index')),
);
// Act
final result = await emailApi.markAsRead(
aliceSession,
AccountFixtures.aliceAccountId,
emails,
emailIds,
ReadActions.markAsRead,
);
@@ -194,7 +194,8 @@ void main() {
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).called(countIterations);
expect(result.length, totalEmails);
expect(result.emailIdsSuccess.length, totalEmails);
expect(result.mapErrors.isEmpty, isTrue);
});
test(
@@ -206,7 +207,7 @@ void main() {
const defaultMaxObjectsInSet = 50;
const maxObjectsInSet = 20;
const totalEmails = 100;
final maxBatches = min(maxObjectsInSet, defaultMaxObjectsInSet);
final maxBatches = [maxObjectsInSet, defaultMaxObjectsInSet, totalEmails].min;
final countIterations = (totalEmails / maxBatches).ceil();
final aliceSession = SessionFixtures.getAliceSessionWithMaxObjectsInSet(maxObjectsInSet);
@@ -248,16 +249,16 @@ void main() {
]
});
final emails = List.generate(
final emailIds = List.generate(
totalEmails,
(index) => Email(id: EmailId(Id('email_$index'))),
(index) => EmailId(Id('email_$index')),
);
// Act
final result = await emailApi.markAsRead(
aliceSession,
AccountFixtures.aliceAccountId,
emails,
emailIds,
ReadActions.markAsRead,
);
@@ -267,7 +268,83 @@ void main() {
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).called(countIterations);
expect(result.length, totalEmails);
expect(result.emailIdsSuccess.length, totalEmails);
expect(result.mapErrors.isEmpty, isTrue);
});
test(
'SHOULD calls execute the correct number of times\n'
'WHEN totalEmails is less than defaultMaxObjectsInSet\n'
'WHEN maxObjectsInSet equal defaultMaxObjectsInSet\n'
'AND defaultMaxObjectsInSet is 50',
() async {
// Arrange
const defaultMaxObjectsInSet = 50;
const maxObjectsInSet = 50;
const totalEmails = 30;
final maxBatches = [maxObjectsInSet, defaultMaxObjectsInSet, totalEmails].min;
final countIterations = (totalEmails / maxBatches).ceil();
final aliceSession = SessionFixtures.getAliceSessionWithMaxObjectsInSet(maxObjectsInSet);
when(httpClient.post(
'',
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).thenAnswer((_) async => {
"sessionState": "2c9f1b12-b35a-43e6-9af2-0106fb53a943",
"methodResponses": [
[
"Email/set",
{
"accountId": AccountFixtures.aliceAccountId.asString,
"oldState": "4cb1e760-c11b-11ef-9cfb-ef2eae0e64b1",
"newState": "4cb1e760-c11b-11ef-9cfb-ef2eae0e64b1",
"updated": {
for (var item in List.generate(maxBatches, (index) => {"email_$index": null}))
item.keys.first: item.values.first
}
},
"c0"
],
[
"Email/get",
{
"accountId": AccountFixtures.aliceAccountId.asString,
"state": "b965db40-c11c-11ef-9cfb-ef2eae0e64b1",
"list": List.generate(maxBatches, (index) => {
"id": "email_$index",
"keywords": {
"\$seen": true
}
}),
"notFound": []
},
"c1"
]
]
});
final emailIds = List.generate(
totalEmails,
(index) => EmailId(Id('email_$index')),
);
// Act
final result = await emailApi.markAsRead(
aliceSession,
AccountFixtures.aliceAccountId,
emailIds,
ReadActions.markAsRead,
);
// Assert
verify(httpClient.post(
'',
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).called(countIterations);
expect(result.emailIdsSuccess.length, totalEmails);
expect(result.mapErrors.isEmpty, isTrue);
});
});
@@ -281,7 +358,7 @@ void main() {
const defaultMaxObjectsInSet = 50;
const maxObjectsInSet = 50;
const totalEmails = 100;
final maxBatches = min(maxObjectsInSet, defaultMaxObjectsInSet);
final maxBatches = [maxObjectsInSet, defaultMaxObjectsInSet, totalEmails].min;
final countIterations = (totalEmails / maxBatches).ceil();
final aliceSession = SessionFixtures.getAliceSessionWithMaxObjectsInSet(maxObjectsInSet);
@@ -323,16 +400,16 @@ void main() {
]
});
final emails = List.generate(
final emailIds = List.generate(
totalEmails,
(index) => Email(id: EmailId(Id('email_$index'))),
(index) => EmailId(Id('email_$index')),
);
// Act
final result = await emailApi.markAsStar(
aliceSession,
AccountFixtures.aliceAccountId,
emails,
emailIds,
MarkStarAction.markStar,
);
@@ -342,7 +419,8 @@ void main() {
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).called(countIterations);
expect(result.length, totalEmails);
expect(result.emailIdsSuccess.length, totalEmails);
expect(result.mapErrors.isEmpty, isTrue);
});
test(
@@ -354,7 +432,7 @@ void main() {
const defaultMaxObjectsInSet = 50;
const maxObjectsInSet = 200;
const totalEmails = 100;
final maxBatches = min(maxObjectsInSet, defaultMaxObjectsInSet);
final maxBatches = [maxObjectsInSet, defaultMaxObjectsInSet, totalEmails].min;
final countIterations = (totalEmails / maxBatches).ceil();
final aliceSession = SessionFixtures.getAliceSessionWithMaxObjectsInSet(maxObjectsInSet);
@@ -396,16 +474,16 @@ void main() {
]
});
final emails = List.generate(
final emailIds = List.generate(
totalEmails,
(index) => Email(id: EmailId(Id('email_$index'))),
(index) => EmailId(Id('email_$index')),
);
// Act
final result = await emailApi.markAsStar(
aliceSession,
AccountFixtures.aliceAccountId,
emails,
emailIds,
MarkStarAction.markStar,
);
@@ -415,7 +493,8 @@ void main() {
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).called(countIterations);
expect(result.length, totalEmails);
expect(result.emailIdsSuccess.length, totalEmails);
expect(result.mapErrors.isEmpty, isTrue);
});
test(
@@ -427,7 +506,7 @@ void main() {
const defaultMaxObjectsInSet = 50;
const maxObjectsInSet = 20;
const totalEmails = 100;
final maxBatches = min(maxObjectsInSet, defaultMaxObjectsInSet);
final maxBatches = [maxObjectsInSet, defaultMaxObjectsInSet, totalEmails].min;
final countIterations = (totalEmails / maxBatches).ceil();
final aliceSession = SessionFixtures.getAliceSessionWithMaxObjectsInSet(maxObjectsInSet);
@@ -469,16 +548,16 @@ void main() {
]
});
final emails = List.generate(
final emailIds = List.generate(
totalEmails,
(index) => Email(id: EmailId(Id('email_$index'))),
(index) => EmailId(Id('email_$index')),
);
// Act
final result = await emailApi.markAsStar(
aliceSession,
AccountFixtures.aliceAccountId,
emails,
emailIds,
MarkStarAction.markStar,
);
@@ -488,7 +567,306 @@ void main() {
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).called(countIterations);
expect(result.length, totalEmails);
expect(result.emailIdsSuccess.length, totalEmails);
expect(result.mapErrors.isEmpty, isTrue);
});
test(
'SHOULD calls execute the correct number of times\n'
'WHEN totalEmails is less than defaultMaxObjectsInSet\n'
'WHEN maxObjectsInSet equal defaultMaxObjectsInSet\n'
'AND defaultMaxObjectsInSet is 50',
() async {
// Arrange
const defaultMaxObjectsInSet = 50;
const maxObjectsInSet = 50;
const totalEmails = 30;
final maxBatches = [maxObjectsInSet, defaultMaxObjectsInSet, totalEmails].min;
final countIterations = (totalEmails / maxBatches).ceil();
final aliceSession = SessionFixtures.getAliceSessionWithMaxObjectsInSet(maxObjectsInSet);
when(httpClient.post(
'',
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).thenAnswer((_) async => {
"sessionState": "2c9f1b12-b35a-43e6-9af2-0106fb53a943",
"methodResponses": [
[
"Email/set",
{
"accountId": AccountFixtures.aliceAccountId.asString,
"oldState": "4cb1e760-c11b-11ef-9cfb-ef2eae0e64b1",
"newState": "4cb1e760-c11b-11ef-9cfb-ef2eae0e64b1",
"updated": {
for (var item in List.generate(maxBatches, (index) => {"email_$index": null}))
item.keys.first: item.values.first
}
},
"c0"
],
[
"Email/get",
{
"accountId": AccountFixtures.aliceAccountId.asString,
"state": "b965db40-c11c-11ef-9cfb-ef2eae0e64b1",
"list": List.generate(maxBatches, (index) => {
"id": "email_$index",
"keywords": {
"\$flagged": true
}
}),
"notFound": []
},
"c1"
]
]
});
final emailIds = List.generate(
totalEmails,
(index) => EmailId(Id('email_$index')),
);
// Act
final result = await emailApi.markAsStar(
aliceSession,
AccountFixtures.aliceAccountId,
emailIds,
MarkStarAction.markStar,
);
// Assert
verify(httpClient.post(
'',
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).called(countIterations);
expect(result.emailIdsSuccess.length, totalEmails);
expect(result.mapErrors.isEmpty, isTrue);
});
});
group('deleteMultipleEmailsPermanently::test', () {
test(
'SHOULD calls execute the correct number of times\n'
'WHEN maxObjectsInSet equal defaultMaxObjectsInSet\n'
'AND defaultMaxObjectsInSet is 50',
() async {
// Arrange
const defaultMaxObjectsInSet = 50;
const maxObjectsInSet = 50;
const totalEmails = 100;
final maxBatches = [maxObjectsInSet, defaultMaxObjectsInSet, totalEmails].min;
final countIterations = (totalEmails / maxBatches).ceil();
final aliceSession = SessionFixtures.getAliceSessionWithMaxObjectsInSet(maxObjectsInSet);
when(httpClient.post(
'',
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).thenAnswer((_) async => {
"sessionState": "2c9f1b12-b35a-43e6-9af2-0106fb53a943",
"methodResponses": [
[
"Email/set",
{
"accountId": AccountFixtures.aliceAccountId.asString,
"oldState": "4cb1e760-c11b-11ef-9cfb-ef2eae0e64b1",
"newState": "4cb1e760-c11b-11ef-9cfb-ef2eae0e64b1",
"destroyed": List.generate(maxBatches, (index) => "email_$index")
},
"c0"
]
]
});
final emailIds = List.generate(
totalEmails,
(index) => EmailId(Id('email_$index')),
);
// Act
final result = await emailApi.deleteMultipleEmailsPermanently(
aliceSession,
AccountFixtures.aliceAccountId,
emailIds,
);
// Assert
verify(httpClient.post(
'',
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).called(countIterations);
expect(result.emailIdsSuccess.length, totalEmails);
expect(result.mapErrors.isEmpty, isTrue);
});
test(
'SHOULD calls execute the correct number of times\n'
'WHEN maxObjectsInSet is greater than defaultMaxObjectsInSet\n'
'AND defaultMaxObjectsInSet is 50',
() async {
// Arrange
const defaultMaxObjectsInSet = 50;
const maxObjectsInSet = 200;
const totalEmails = 100;
final maxBatches = [maxObjectsInSet, defaultMaxObjectsInSet, totalEmails].min;
final countIterations = (totalEmails / maxBatches).ceil();
final aliceSession = SessionFixtures.getAliceSessionWithMaxObjectsInSet(maxObjectsInSet);
when(httpClient.post(
'',
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).thenAnswer((_) async => {
"sessionState": "2c9f1b12-b35a-43e6-9af2-0106fb53a943",
"methodResponses": [
[
"Email/set",
{
"accountId": AccountFixtures.aliceAccountId.asString,
"oldState": "4cb1e760-c11b-11ef-9cfb-ef2eae0e64b1",
"newState": "4cb1e760-c11b-11ef-9cfb-ef2eae0e64b1",
"destroyed": List.generate(maxBatches, (index) => "email_$index")
},
"c0"
]
]
});
final emailIds = List.generate(
totalEmails,
(index) => EmailId(Id('email_$index')),
);
// Act
final result = await emailApi.deleteMultipleEmailsPermanently(
aliceSession,
AccountFixtures.aliceAccountId,
emailIds,
);
// Assert
verify(httpClient.post(
'',
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).called(countIterations);
expect(result.emailIdsSuccess.length, totalEmails);
expect(result.mapErrors.isEmpty, isTrue);
});
test(
'SHOULD calls execute the correct number of times\n'
'WHEN maxObjectsInSet is less than defaultMaxObjectsInSet\n'
'AND defaultMaxObjectsInSet is 50',
() async {
// Arrange
const defaultMaxObjectsInSet = 50;
const maxObjectsInSet = 20;
const totalEmails = 100;
final maxBatches = [maxObjectsInSet, defaultMaxObjectsInSet, totalEmails].min;
final countIterations = (totalEmails / maxBatches).ceil();
final aliceSession = SessionFixtures.getAliceSessionWithMaxObjectsInSet(maxObjectsInSet);
when(httpClient.post(
'',
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).thenAnswer((_) async => {
"sessionState": "2c9f1b12-b35a-43e6-9af2-0106fb53a943",
"methodResponses": [
[
"Email/set",
{
"accountId": AccountFixtures.aliceAccountId.asString,
"oldState": "4cb1e760-c11b-11ef-9cfb-ef2eae0e64b1",
"newState": "4cb1e760-c11b-11ef-9cfb-ef2eae0e64b1",
"destroyed": List.generate(maxBatches, (index) => "email_$index")
},
"c0"
]
]
});
final emailIds = List.generate(
totalEmails,
(index) => EmailId(Id('email_$index')),
);
// Act
final result = await emailApi.deleteMultipleEmailsPermanently(
aliceSession,
AccountFixtures.aliceAccountId,
emailIds,
);
// Assert
verify(httpClient.post(
'',
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).called(countIterations);
expect(result.emailIdsSuccess.length, totalEmails);
expect(result.mapErrors.isEmpty, isTrue);
});
test(
'SHOULD calls execute the correct number of times\n'
'WHEN totalEmails is less than defaultMaxObjectsInSet\n'
'WHEN maxObjectsInSet equal defaultMaxObjectsInSet\n'
'AND defaultMaxObjectsInSet is 50',
() async {
// Arrange
const defaultMaxObjectsInSet = 50;
const maxObjectsInSet = 50;
const totalEmails = 30;
final maxBatches = [maxObjectsInSet, defaultMaxObjectsInSet, totalEmails].min;
final countIterations = (totalEmails / maxBatches).ceil();
final aliceSession = SessionFixtures.getAliceSessionWithMaxObjectsInSet(maxObjectsInSet);
when(httpClient.post(
'',
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).thenAnswer((_) async => {
"sessionState": "2c9f1b12-b35a-43e6-9af2-0106fb53a943",
"methodResponses": [
[
"Email/set",
{
"accountId": AccountFixtures.aliceAccountId.asString,
"oldState": "4cb1e760-c11b-11ef-9cfb-ef2eae0e64b1",
"newState": "4cb1e760-c11b-11ef-9cfb-ef2eae0e64b1",
"destroyed": List.generate(maxBatches, (index) => "email_$index")
},
"c0"
]
]
});
final emailIds = List.generate(
totalEmails,
(index) => EmailId(Id('email_$index')),
);
// Act
final result = await emailApi.deleteMultipleEmailsPermanently(
aliceSession,
AccountFixtures.aliceAccountId,
emailIds,
);
// Assert
verify(httpClient.post(
'',
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).called(countIterations);
expect(result.emailIdsSuccess.length, totalEmails);
expect(result.mapErrors.isEmpty, isTrue);
});
});
@@ -502,7 +880,7 @@ void main() {
const defaultMaxObjectsInSet = 50;
const maxObjectsInSet = 50;
const totalEmails = 100;
final maxBatches = min(maxObjectsInSet, defaultMaxObjectsInSet);
final maxBatches = [maxObjectsInSet, defaultMaxObjectsInSet, totalEmails].min;
final countIterations = (totalEmails / maxBatches).ceil();
final aliceSession = SessionFixtures.getAliceSessionWithMaxObjectsInSet(maxObjectsInSet);
@@ -554,7 +932,8 @@ void main() {
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).called(countIterations);
expect(result.length, totalEmails);
expect(result.emailIdsSuccess.length, totalEmails);
expect(result.mapErrors.isEmpty, isTrue);
});
test(
@@ -566,7 +945,7 @@ void main() {
const defaultMaxObjectsInSet = 50;
const maxObjectsInSet = 200;
const totalEmails = 100;
final maxBatches = min(maxObjectsInSet, defaultMaxObjectsInSet);
final maxBatches = [maxObjectsInSet, defaultMaxObjectsInSet, totalEmails].min;
final countIterations = (totalEmails / maxBatches).ceil();
final aliceSession = SessionFixtures.getAliceSessionWithMaxObjectsInSet(maxObjectsInSet);
@@ -618,7 +997,8 @@ void main() {
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).called(countIterations);
expect(result.length, totalEmails);
expect(result.emailIdsSuccess.length, totalEmails);
expect(result.mapErrors.isEmpty, isTrue);
});
test(
@@ -630,7 +1010,7 @@ void main() {
const defaultMaxObjectsInSet = 50;
const maxObjectsInSet = 20;
const totalEmails = 100;
final maxBatches = min(maxObjectsInSet, defaultMaxObjectsInSet);
final maxBatches = [maxObjectsInSet, defaultMaxObjectsInSet, totalEmails].min;
final countIterations = (totalEmails / maxBatches).ceil();
final aliceSession = SessionFixtures.getAliceSessionWithMaxObjectsInSet(maxObjectsInSet);
@@ -682,7 +1062,74 @@ void main() {
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).called(countIterations);
expect(result.length, totalEmails);
expect(result.emailIdsSuccess.length, totalEmails);
expect(result.mapErrors.isEmpty, isTrue);
});
test(
'SHOULD calls execute the correct number of times\n'
'WHEN totalEmails is less than defaultMaxObjectsInSet\n'
'WHEN maxObjectsInSet equal defaultMaxObjectsInSet\n'
'AND defaultMaxObjectsInSet is 50',
() async {
// Arrange
const defaultMaxObjectsInSet = 50;
const maxObjectsInSet = 50;
const totalEmails = 30;
final maxBatches = [maxObjectsInSet, defaultMaxObjectsInSet, totalEmails].min;
final countIterations = (totalEmails / maxBatches).ceil();
final aliceSession = SessionFixtures.getAliceSessionWithMaxObjectsInSet(maxObjectsInSet);
when(httpClient.post(
'',
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).thenAnswer((_) async => {
"sessionState": "2c9f1b12-b35a-43e6-9af2-0106fb53a943",
"methodResponses": [
[
"Email/set",
{
"accountId": AccountFixtures.aliceAccountId.asString,
"oldState": "4cb1e760-c11b-11ef-9cfb-ef2eae0e64b1",
"newState": "4cb1e760-c11b-11ef-9cfb-ef2eae0e64b1",
"updated": {
for (var item in List.generate(maxBatches, (index) => {"email_$index": null}))
item.keys.first: item.values.first
}
},
"c0"
]
]
});
final emailIds = List.generate(
totalEmails,
(index) => EmailId(Id('email_$index')),
);
// Act
final result = await emailApi.moveToMailbox(
aliceSession,
AccountFixtures.aliceAccountId,
MoveToMailboxRequest(
{
MailboxId(Id('mailboxA')): emailIds,
},
MailboxId(Id('mailboxB')),
MoveAction.moving,
EmailActionType.moveToMailbox,
),
);
// Assert
verify(httpClient.post(
'',
data: anyNamed('data'),
cancelToken: anyNamed('cancelToken'),
)).called(countIterations);
expect(result.emailIdsSuccess.length, totalEmails);
expect(result.mapErrors.isEmpty, isTrue);
});
});
});