fix(sentry): resolve minified exception types on web release

This commit is contained in:
dab246
2026-03-03 11:43:45 +07:00
committed by Dat H. Pham
parent 2e8a11e7eb
commit 437c46f5ff
53 changed files with 133 additions and 109 deletions
+2 -1
View File
@@ -20,10 +20,11 @@ ENV GITHUB_SHA=$GITHUB_SHA \
SENTRY_RELEASE=$SENTRY_RELEASE SENTRY_RELEASE=$SENTRY_RELEASE
RUN ./scripts/prebuild.sh && \ RUN ./scripts/prebuild.sh && \
if [ -z "$GITHUB_SHA" ]; then echo "GITHUB_SHA is required for SENTRY_DIST"; exit 1; fi && \
flutter build web --release --source-maps \ flutter build web --release --source-maps \
--dart-define=SENTRY_RELEASE=$SENTRY_RELEASE \ --dart-define=SENTRY_RELEASE=$SENTRY_RELEASE \
--dart-define=SENTRY_DIST=$GITHUB_SHA && \ --dart-define=SENTRY_DIST=$GITHUB_SHA && \
if [ -n "$SENTRY_AUTH_TOKEN" ] && [ -n "$SENTRY_ORG" ] && [ -n "$SENTRY_PROJECT" ] && [ -n "$SENTRY_RELEASE" ]; then \ if [ -n "$SENTRY_AUTH_TOKEN" ] && [ -n "$SENTRY_ORG" ] && [ -n "$SENTRY_PROJECT" ] && [ -n "$SENTRY_RELEASE" ] && [ -n "$GITHUB_SHA" ]; then \
echo "Sentry configuration detected, uploading sourcemaps..." && \ echo "Sentry configuration detected, uploading sourcemaps..." && \
curl -sL https://sentry.io/get-cli/ | SENTRY_CLI_VERSION=2.20.7 bash && \ curl -sL https://sentry.io/get-cli/ | SENTRY_CLI_VERSION=2.20.7 bash && \
sentry-cli releases new "$SENTRY_RELEASE" && \ sentry-cli releases new "$SENTRY_RELEASE" && \
@@ -1,12 +1,8 @@
import 'package:core/domain/exceptions/app_base_exception.dart'; import 'package:core/domain/exceptions/app_base_exception.dart';
import 'package:equatable/equatable.dart';
class AddressException extends AppBaseException with EquatableMixin { class AddressException extends AppBaseException {
const AddressException(super.message); const AddressException(super.message);
@override @override
String get exceptionName => 'AddressException'; String get exceptionName => 'AddressException';
@override
List<Object?> get props => [message, exceptionName];
} }
@@ -1,15 +1,20 @@
abstract class AppBaseException implements Exception { import 'package:equatable/equatable.dart';
abstract class AppBaseException with EquatableMixin implements Exception {
final String? message; final String? message;
const AppBaseException([this.message]); const AppBaseException([this.message]);
@override @override
String toString() { String toString() {
if (message != null && message!.isNotEmpty) { if (message?.isNotEmpty == true) {
return '$exceptionName: $message'; return '$exceptionName: $message';
} }
return exceptionName; return exceptionName;
} }
String get exceptionName; String get exceptionName;
@override
List<Object?> get props => [message];
} }
@@ -1,12 +1,7 @@
import 'package:core/domain/exceptions/app_base_exception.dart'; import 'package:core/domain/exceptions/app_base_exception.dart';
import 'package:equatable/equatable.dart';
abstract class DownloadFileException extends AppBaseException abstract class DownloadFileException extends AppBaseException {
with EquatableMixin {
const DownloadFileException(super.message); const DownloadFileException(super.message);
@override
List<Object?> get props => [message, exceptionName];
} }
class CommonDownloadFileException extends DownloadFileException { class CommonDownloadFileException extends DownloadFileException {
@@ -1,11 +1,7 @@
import 'package:core/domain/exceptions/app_base_exception.dart'; import 'package:core/domain/exceptions/app_base_exception.dart';
import 'package:equatable/equatable.dart';
abstract class FileException extends AppBaseException with EquatableMixin { abstract class FileException extends AppBaseException {
const FileException(super.message); const FileException(super.message);
@override
List<Object?> get props => [message, exceptionName];
} }
class NotFoundFileInFolderException extends FileException { class NotFoundFileInFolderException extends FileException {
@@ -1,14 +1,10 @@
import 'package:core/domain/exceptions/app_base_exception.dart'; import 'package:core/domain/exceptions/app_base_exception.dart';
import 'package:equatable/equatable.dart';
class PlatformException extends AppBaseException with EquatableMixin { class PlatformException extends AppBaseException {
const PlatformException(super.message); const PlatformException(super.message);
@override @override
String get exceptionName => 'PlatformException'; String get exceptionName => 'PlatformException';
@override
List<Object?> get props => [message, exceptionName];
} }
class NoSupportPlatformException extends PlatformException { class NoSupportPlatformException extends PlatformException {
+1 -1
View File
@@ -88,7 +88,7 @@ class MailAddress with EquatableMixin {
if (postChar == '.') { if (postChar == '.') {
var lastChar = address[pos - 1]; var lastChar = address[pos - 1];
if (lastChar == '@' || lastChar == '.') { if (lastChar == '@' || lastChar == '.') {
throw const AddressException('Subdomain expected before "." or duplicate "." in "address"'); throw AddressException('Subdomain expected before "." or duplicate "." in "$address"');
} }
domainSB.write('.'); domainSB.write('.');
pos++; pos++;
+6
View File
@@ -1,6 +1,7 @@
import 'package:core/utils/application_manager.dart'; import 'package:core/utils/application_manager.dart';
import 'package:core/utils/build_utils.dart'; import 'package:core/utils/build_utils.dart';
import 'package:core/utils/config/env_loader.dart'; import 'package:core/utils/config/env_loader.dart';
import 'package:core/utils/app_logger.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart';
/// Holds configuration values for initializing Sentry. /// Holds configuration values for initializing Sentry.
@@ -69,6 +70,11 @@ class SentryConfig {
final appVersion = await ApplicationManager().getAppVersion(); final appVersion = await ApplicationManager().getAppVersion();
const sentryDist = String.fromEnvironment('SENTRY_DIST'); const sentryDist = String.fromEnvironment('SENTRY_DIST');
logTrace(
'SentryConfig::load: sentryDist is $sentryDist,'
'appVersion is $appVersion',
webConsoleEnabled: true,
);
return SentryConfig( return SentryConfig(
dsn: sentryDSN, dsn: sentryDSN,
+39 -12
View File
@@ -55,27 +55,54 @@ class SentryInitializer {
SentryEvent event, SentryEvent event,
Hint? hint, Hint? hint,
) async { ) async {
final req = event.request; event.request = _sanitizeRequest(event.request);
if (req == null) return event; event.exceptions = _deminifyExceptions(event.exceptions);
final sanitizedHeaders = Map<String, String>.from(req.headers) return event;
..removeWhere( }
(k, _) => _blockedHeaderPatterns.any(
(p) => k.toLowerCase().contains(p),
),
);
final sanitizedRequest = SentryRequest( static SentryRequest? _sanitizeRequest(SentryRequest? req) {
if (req == null) return null;
return SentryRequest(
url: req.url, url: req.url,
method: req.method, method: req.method,
headers: sanitizedHeaders, headers: _sanitizeHeaders(req.headers),
queryString: req.queryString, queryString: req.queryString,
cookies: null, cookies: null,
data: null, data: null,
); );
}
event.request = sanitizedRequest; static Map<String, String> _sanitizeHeaders(Map<String, String> headers) {
return Map<String, String>.from(headers)
..removeWhere(
(key, _) => _blockedHeaderPatterns.any(
(pattern) => key.toLowerCase().contains(pattern),
),
);
}
return event; static List<SentryException>? _deminifyExceptions(
List<SentryException>? exceptions,
) {
if (exceptions == null) return null;
return exceptions.map((e) {
if (e.type?.startsWith('minified:') == true) {
final rawValue = e.value?.trim() ?? '';
final extractedType = RegExp(r'^([A-Za-z_][A-Za-z0-9_]*)\s*:')
.firstMatch(rawValue)
?.group(1) ??
RegExp(r"Instance of '([^']+)'").firstMatch(rawValue)?.group(1);
if (extractedType != null &&
extractedType.isNotEmpty &&
extractedType != 'minified' &&
!extractedType.startsWith('minified:')) {
e.type = extractedType;
}
}
return e;
}).toList();
} }
} }
@@ -1,7 +1,7 @@
import 'package:core/domain/exceptions/app_base_exception.dart'; import 'package:core/domain/exceptions/app_base_exception.dart';
class NotFoundDataWithThisKeyException extends AppBaseException { class NotFoundDataWithThisKeyException extends AppBaseException {
NotFoundDataWithThisKeyException([super.message]); const NotFoundDataWithThisKeyException([super.message]);
@override @override
String get exceptionName => 'NotFoundDataWithThisKeyException'; String get exceptionName => 'NotFoundDataWithThisKeyException';
@@ -17,7 +17,7 @@ class LocalStorageManager {
if (entry != null) { if (entry != null) {
return entry.value; return entry.value;
} else { } else {
throw NotFoundDataWithThisKeyException(); throw const NotFoundDataWithThisKeyException();
} }
} }
@@ -19,7 +19,7 @@ class SessionStorageManager {
if (entry != null) { if (entry != null) {
return entry.value; return entry.value;
} else { } else {
throw NotFoundDataWithThisKeyException(); throw const NotFoundDataWithThisKeyException();
} }
} }
@@ -82,7 +82,7 @@ extension HandleLabelForEmailExtension on SingleEmailController {
emitFailure( emitFailure(
controller: this, controller: this,
failure: AddALabelToAnEmailFailure( failure: AddALabelToAnEmailFailure(
exception: LabelKeywordIsNull(), exception: const LabelKeywordIsNull(),
labelDisplay: labelDisplay, labelDisplay: labelDisplay,
), ),
); );
@@ -290,7 +290,7 @@ extension HandleLabelForEmailExtension on SingleEmailController {
emitFailure( emitFailure(
controller: this, controller: this,
failure: RemoveALabelFromAnEmailFailure( failure: RemoveALabelFromAnEmailFailure(
exception: LabelKeywordIsNull(), exception: const LabelKeywordIsNull(),
labelDisplay: labelDisplay, labelDisplay: labelDisplay,
), ),
); );
@@ -110,7 +110,7 @@ class LabelApi
final labelId = label.id; final labelId = label.id;
if (labelId == null) { if (labelId == null) {
throw LabelIdIsNull(); throw const LabelIdIsNull();
} }
final method = SetLabelMethod(accountId)..addDestroy({labelId}); final method = SetLabelMethod(accountId)..addDestroy({labelId});
@@ -1,14 +1,14 @@
import 'package:core/domain/exceptions/app_base_exception.dart'; import 'package:core/domain/exceptions/app_base_exception.dart';
class LabelKeywordIsNull extends AppBaseException { class LabelKeywordIsNull extends AppBaseException {
LabelKeywordIsNull([super.message = 'Label keyword is null']); const LabelKeywordIsNull([super.message = 'Label keyword is null']);
@override @override
String get exceptionName => 'LabelKeywordIsNull'; String get exceptionName => 'LabelKeywordIsNull';
} }
class LabelIdIsNull extends AppBaseException { class LabelIdIsNull extends AppBaseException {
LabelIdIsNull([super.message = 'Label id is null']); const LabelIdIsNull([super.message = 'Label id is null']);
@override @override
String get exceptionName => 'LabelIdIsNull'; String get exceptionName => 'LabelIdIsNull';
@@ -93,7 +93,7 @@ extension HandleLabelActionTypeExtension on LabelController {
if (labelId == null) { if (labelId == null) {
consumeState( consumeState(
Stream.value(Left(EditLabelFailure(LabelIdIsNull()))), Stream.value(Left(EditLabelFailure(const LabelIdIsNull()))),
); );
return; return;
} }
@@ -55,7 +55,7 @@ class DownloadAttachmentHasTokenExpiredException extends AppBaseException {
final String refreshToken; final String refreshToken;
DownloadAttachmentHasTokenExpiredException(this.refreshToken) DownloadAttachmentHasTokenExpiredException(this.refreshToken)
: super('Token expired for refresh token: $refreshToken'); : super('Token expired for refresh token');
@override @override
String get exceptionName => 'DownloadAttachmentHasTokenExpiredException'; String get exceptionName => 'DownloadAttachmentHasTokenExpiredException';
@@ -102,3 +102,10 @@ class SaasServerUriIsNull extends AppBaseException {
@override @override
String get exceptionName => 'SaasServerUriIsNull'; String get exceptionName => 'SaasServerUriIsNull';
} }
class AutoRedirectToAppAfterStoreAuthorizeDestinationUrlException extends AppBaseException {
AutoRedirectToAppAfterStoreAuthorizeDestinationUrlException([super.message]);
@override
String get exceptionName => 'AutoRedirectToAppAfterStoreAuthorizeDestinationUrlException';
}
@@ -438,7 +438,7 @@ class MailboxAPI with HandleSetErrorMixin, SessionMixin, MailAPIMixin {
if (setMailboxResponse?.updated?.containsKey(request.mailboxId.id) ?? false) { if (setMailboxResponse?.updated?.containsKey(request.mailboxId.id) ?? false) {
return true; return true;
} else { } else {
throw SetMailboxRightsException(); throw const SetMailboxRightsException();
} }
} }
@@ -1,7 +1,7 @@
import 'package:core/domain/exceptions/app_base_exception.dart'; import 'package:core/domain/exceptions/app_base_exception.dart';
class NullSessionOrAccountIdException extends AppBaseException { class NullSessionOrAccountIdException extends AppBaseException {
NullSessionOrAccountIdException( const NullSessionOrAccountIdException(
[super.message = 'session and accountId should not be null']); [super.message = 'session and accountId should not be null']);
@override @override
@@ -1,7 +1,7 @@
import 'package:core/domain/exceptions/app_base_exception.dart'; import 'package:core/domain/exceptions/app_base_exception.dart';
class SetMailboxRightsException extends AppBaseException { class SetMailboxRightsException extends AppBaseException {
SetMailboxRightsException( const SetMailboxRightsException(
[super.message = 'Failed to update mailbox rights.']); [super.message = 'Failed to update mailbox rights.']);
@override @override
@@ -1507,7 +1507,7 @@ class MailboxController extends BaseMailboxController
consumeState(_subaddressingInteractor.execute(session, accountId, allowSubaddressingRequest)); consumeState(_subaddressingInteractor.execute(session, accountId, allowSubaddressingRequest));
} else { } else {
handleSubAddressingFailure( handleSubAddressingFailure(
SubaddressingFailure.withException(NullSessionOrAccountIdException()), SubaddressingFailure.withException(const NullSessionOrAccountIdException()),
); );
} }
@@ -1,8 +1,6 @@
import 'package:equatable/equatable.dart';
import 'package:core/domain/exceptions/app_base_exception.dart'; import 'package:core/domain/exceptions/app_base_exception.dart';
abstract class VerifyNameException extends AppBaseException abstract class VerifyNameException extends AppBaseException {
with EquatableMixin {
static const emptyName = 'The name cannot be empty!'; static const emptyName = 'The name cannot be empty!';
static const duplicatedName = 'The name already exists!'; static const duplicatedName = 'The name already exists!';
static const nameContainSpecialCharacter = static const nameContainSpecialCharacter =
@@ -37,7 +37,7 @@ class ForwardingAPI with HandleSetErrorMixin {
final tMailForwardResult = result?.list.firstOrNull; final tMailForwardResult = result?.list.firstOrNull;
if (tMailForwardResult == null) { if (tMailForwardResult == null) {
throw NotFoundForwardException(); throw const NotFoundForwardException();
} }
return tMailForwardResult; return tMailForwardResult;
@@ -86,6 +86,6 @@ class ForwardingAPI with HandleSetErrorMixin {
return (newForward, methodException); return (newForward, methodException);
} }
throw methodException ?? NotFoundForwardException(); throw methodException ?? const NotFoundForwardException();
} }
} }
@@ -1,28 +1,28 @@
import 'package:core/domain/exceptions/app_base_exception.dart'; import 'package:core/domain/exceptions/app_base_exception.dart';
class NotFoundForwardException extends AppBaseException { class NotFoundForwardException extends AppBaseException {
NotFoundForwardException([super.message]); const NotFoundForwardException([super.message]);
@override @override
String get exceptionName => 'NotFoundForwardException'; String get exceptionName => 'NotFoundForwardException';
} }
class UpdateForwardException extends AppBaseException { class UpdateForwardException extends AppBaseException {
UpdateForwardException([super.message]); const UpdateForwardException([super.message]);
@override @override
String get exceptionName => 'UpdateForwardException'; String get exceptionName => 'UpdateForwardException';
} }
class RecipientListIsEmptyException extends AppBaseException { class RecipientListIsEmptyException extends AppBaseException {
RecipientListIsEmptyException([super.message]); const RecipientListIsEmptyException([super.message]);
@override @override
String get exceptionName => 'RecipientListIsEmptyException'; String get exceptionName => 'RecipientListIsEmptyException';
} }
class RecipientListWithInvalidEmailsException extends AppBaseException { class RecipientListWithInvalidEmailsException extends AppBaseException {
RecipientListWithInvalidEmailsException([super.message]); const RecipientListWithInvalidEmailsException([super.message]);
@override @override
String get exceptionName => 'RecipientListWithInvalidEmailsException'; String get exceptionName => 'RecipientListWithInvalidEmailsException';
@@ -37,7 +37,7 @@ extension VerifyDefaultIdentitySupportedExtension on IdentitiesController {
} else { } else {
consumeState( consumeState(
Stream.value( Stream.value(
Left(EditDefaultIdentityFailure(NullSessionOrAccountIdException())), Left(EditDefaultIdentityFailure(const NullSessionOrAccountIdException())),
), ),
); );
} }
@@ -301,7 +301,7 @@ class _AutocompleteContactTextFieldWithTagsState extends State<AutocompleteConta
final emailAddress = EmailAddress(null, inputText); final emailAddress = EmailAddress(null, inputText);
if (!_validateEmailAddressIsValid(emailAddress)) { if (!_validateEmailAddressIsValid(emailAddress)) {
widget.onExceptionCallback?.call(RecipientListWithInvalidEmailsException()); widget.onExceptionCallback?.call(const RecipientListWithInvalidEmailsException());
_resetInputText(); _resetInputText();
return; return;
} }
@@ -350,12 +350,12 @@ class _AutocompleteContactTextFieldWithTagsState extends State<AutocompleteConta
} }
if (listEmailAddress.isEmpty) { if (listEmailAddress.isEmpty) {
widget.onExceptionCallback?.call(RecipientListIsEmptyException()); widget.onExceptionCallback?.call(const RecipientListIsEmptyException());
return; return;
} }
if (!_validateListEmailAddressIsValid(listEmailAddress)) { if (!_validateListEmailAddressIsValid(listEmailAddress)) {
widget.onExceptionCallback?.call(RecipientListWithInvalidEmailsException()); widget.onExceptionCallback?.call(const RecipientListWithInvalidEmailsException());
return; return;
} }
@@ -414,7 +414,7 @@ class _AutocompleteContactTextFieldWithTagsState extends State<AutocompleteConta
}) { }) {
log('_AutocompleteContactTextFieldWithTagsState::_addEmailAddressToInputFieldAction:emailAddress = $emailAddress'); log('_AutocompleteContactTextFieldWithTagsState::_addEmailAddressToInputFieldAction:emailAddress = $emailAddress');
if (!_validateEmailAddressIsValid(emailAddress)) { if (!_validateEmailAddressIsValid(emailAddress)) {
widget.onExceptionCallback?.call(RecipientListWithInvalidEmailsException()); widget.onExceptionCallback?.call(const RecipientListWithInvalidEmailsException());
if (isClearInput) { if (isClearInput) {
_resetInputText(); _resetInputText();
} }
@@ -238,7 +238,7 @@ class VacationController extends BaseController {
consumeState(_updateVacationInteractor.execute(accountId, vacationResponse)); consumeState(_updateVacationInteractor.execute(accountId, vacationResponse));
} else { } else {
consumeState( consumeState(
Stream.value(Left(UpdateVacationFailure(NullSessionOrAccountIdException()))), Stream.value(Left(UpdateVacationFailure(const NullSessionOrAccountIdException()))),
); );
} }
} }
@@ -101,7 +101,7 @@ class PublicAssetApi {
if (notCreatedError == null && publicAsset != null) return publicAsset; if (notCreatedError == null && publicAsset != null) return publicAsset;
if (notCreatedError?.type == ErrorMethodResponse.overQuota) { if (notCreatedError?.type == ErrorMethodResponse.overQuota) {
throw PublicAssetQuotaExceededException(message: notCreatedError?.description); throw PublicAssetQuotaExceededException(notCreatedError?.description);
} }
throw const CannotCreatePublicAssetException(); throw const CannotCreatePublicAssetException();
@@ -8,7 +8,7 @@ class CannotCreatePublicAssetException extends AppBaseException {
} }
class PublicAssetQuotaExceededException extends AppBaseException { class PublicAssetQuotaExceededException extends AppBaseException {
const PublicAssetQuotaExceededException({String? message}) : super(message); const PublicAssetQuotaExceededException([super.message]);
@override @override
String get exceptionName => 'PublicAssetQuotaExceededException'; String get exceptionName => 'PublicAssetQuotaExceededException';
@@ -28,7 +28,7 @@ class QuotasAPI {
if (getQuotaResponse?.list.isNotEmpty == true) { if (getQuotaResponse?.list.isNotEmpty == true) {
return getQuotaResponse!.list; return getQuotaResponse!.list;
} else { } else {
throw NotFoundQuotasException(); throw const NotFoundQuotasException();
} }
} }
} }
@@ -1,14 +1,14 @@
import 'package:core/domain/exceptions/app_base_exception.dart'; import 'package:core/domain/exceptions/app_base_exception.dart';
class NotFoundQuotasException extends AppBaseException { class NotFoundQuotasException extends AppBaseException {
NotFoundQuotasException([super.message]); const NotFoundQuotasException([super.message]);
@override @override
String get exceptionName => 'NotFoundQuotasException'; String get exceptionName => 'NotFoundQuotasException';
} }
class QuotasNotSupportedException extends AppBaseException { class QuotasNotSupportedException extends AppBaseException {
QuotasNotSupportedException([super.message]); const QuotasNotSupportedException([super.message]);
@override @override
String get exceptionName => 'QuotasNotSupportedException'; String get exceptionName => 'QuotasNotSupportedException';
@@ -494,7 +494,7 @@ class SearchMailboxController extends BaseMailboxController with MailboxActionHa
)); ));
} else { } else {
handleSubAddressingFailure( handleSubAddressingFailure(
SubaddressingFailure.withException(NullSessionOrAccountIdException()), SubaddressingFailure.withException(const NullSessionOrAccountIdException()),
); );
} }
@@ -39,7 +39,7 @@ class ServerSettingsAPI with HandleSetErrorMixin {
)?.list.firstOrNull; )?.list.firstOrNull;
if (serverSettings == null) { if (serverSettings == null) {
throw NotFoundServerSettingsException(); throw const NotFoundServerSettingsException();
} }
return serverSettings; return serverSettings;
@@ -81,10 +81,10 @@ class ServerSettingsAPI with HandleSetErrorMixin {
)?.updated ?? {}; )?.updated ?? {};
if (updateServerSettingsResult.isEmpty) { if (updateServerSettingsResult.isEmpty) {
throw CanNotUpdateServerSettingsException(); throw const CanNotUpdateServerSettingsException();
} }
} on ErrorMethodResponseException catch (_) { } on ErrorMethodResponseException catch (_) {
throw CanNotUpdateServerSettingsException(); throw const CanNotUpdateServerSettingsException();
} }
try { try {
@@ -94,12 +94,12 @@ class ServerSettingsAPI with HandleSetErrorMixin {
)?.list.firstOrNull; )?.list.firstOrNull;
if (updatedServerSettings == null) { if (updatedServerSettings == null) {
throw CanNotUpdateServerSettingsException(); throw const CanNotUpdateServerSettingsException();
} }
return updatedServerSettings; return updatedServerSettings;
} on ErrorMethodResponseException catch (_) { } on ErrorMethodResponseException catch (_) {
throw CanNotUpdateServerSettingsException(); throw const CanNotUpdateServerSettingsException();
} }
} }
} }
@@ -1,21 +1,21 @@
import 'package:core/domain/exceptions/app_base_exception.dart'; import 'package:core/domain/exceptions/app_base_exception.dart';
class NotFoundServerSettingsException extends AppBaseException { class NotFoundServerSettingsException extends AppBaseException {
NotFoundServerSettingsException([super.message]); const NotFoundServerSettingsException([super.message]);
@override @override
String get exceptionName => 'NotFoundServerSettingsException'; String get exceptionName => 'NotFoundServerSettingsException';
} }
class NotFoundSettingOptionException extends AppBaseException { class NotFoundSettingOptionException extends AppBaseException {
NotFoundSettingOptionException([super.message]); const NotFoundSettingOptionException([super.message]);
@override @override
String get exceptionName => 'NotFoundSettingOptionException'; String get exceptionName => 'NotFoundSettingOptionException';
} }
class CanNotUpdateServerSettingsException extends AppBaseException { class CanNotUpdateServerSettingsException extends AppBaseException {
CanNotUpdateServerSettingsException([super.message]); const CanNotUpdateServerSettingsException([super.message]);
@override @override
String get exceptionName => 'CanNotUpdateServerSettingsException'; String get exceptionName => 'CanNotUpdateServerSettingsException';
@@ -17,7 +17,7 @@ class GetServerSettingInteractor {
final serverSetting = await _serverSettingsRepository.getServerSettings(accountId); final serverSetting = await _serverSettingsRepository.getServerSettings(accountId);
final settingOption = serverSetting.settings; final settingOption = serverSetting.settings;
if (settingOption == null) { if (settingOption == null) {
yield Left(GetServerSettingFailure(NotFoundSettingOptionException())); yield Left(GetServerSettingFailure(const NotFoundSettingOptionException()));
} else { } else {
yield Right(GetServerSettingSuccess(settingOption)); yield Right(GetServerSettingSuccess(settingOption));
} }
@@ -27,7 +27,7 @@ class UpdateServerSettingInteractor {
); );
final settingOption = serverSetting.settings; final settingOption = serverSetting.settings;
if (settingOption == null) { if (settingOption == null) {
yield Left(UpdateServerSettingFailure(NotFoundServerSettingsException())); yield Left(UpdateServerSettingFailure(const NotFoundServerSettingsException()));
} else { } else {
yield Right(UpdateServerSettingSuccess(settingOption)); yield Right(UpdateServerSettingSuccess(settingOption));
} }
@@ -32,7 +32,7 @@ class ThreadDetailRepositoryImpl implements ThreadDetailRepository {
.getThreadById(threadId, accountId); .getThreadById(threadId, accountId);
if (originalEmailIds.isEmpty) { if (originalEmailIds.isEmpty) {
throw EmptyThreadDetailException(); throw const EmptyThreadDetailException();
} }
final filteredEmailIds = await Future.wait( final filteredEmailIds = await Future.wait(
@@ -1,7 +1,7 @@
import 'package:core/domain/exceptions/app_base_exception.dart'; import 'package:core/domain/exceptions/app_base_exception.dart';
class EmptyThreadDetailException extends AppBaseException { class EmptyThreadDetailException extends AppBaseException {
EmptyThreadDetailException([super.message]); const EmptyThreadDetailException([super.message]);
@override @override
String get exceptionName => 'EmptyThreadDetailException'; String get exceptionName => 'EmptyThreadDetailException';
@@ -112,7 +112,7 @@ extension AddLabelToThreadExtension on ThreadDetailController {
consumeState( consumeState(
Stream.value(Left( Stream.value(Left(
AddALabelToAThreadFailure( AddALabelToAThreadFailure(
exception: LabelKeywordIsNull(), exception: const LabelKeywordIsNull(),
labelDisplay: labelDisplay, labelDisplay: labelDisplay,
), ),
)), )),
@@ -44,7 +44,7 @@ extension RemoveLabelFromThreadExtension on ThreadDetailController {
emitFailure( emitFailure(
controller: this, controller: this,
failure: RemoveALabelFromAThreadFailure( failure: RemoveALabelFromAThreadFailure(
exception: LabelKeywordIsNull(), exception: const LabelKeywordIsNull(),
labelDisplay: labelDisplay, labelDisplay: labelDisplay,
), ),
); );
@@ -226,7 +226,7 @@ class FileUploader {
charset: fileCharset); charset: fileCharset);
} else { } else {
logWarning('FileUploader::_parsingResponse(): DataResponseIsNullException'); logWarning('FileUploader::_parsingResponse(): DataResponseIsNullException');
throw DataResponseIsNullException(); throw const DataResponseIsNullException();
} }
} }
} }
@@ -1,7 +1,7 @@
import 'package:core/domain/exceptions/app_base_exception.dart'; import 'package:core/domain/exceptions/app_base_exception.dart';
class PickFileCanceledException extends AppBaseException { class PickFileCanceledException extends AppBaseException {
PickFileCanceledException([super.message]); const PickFileCanceledException([super.message]);
@override @override
String get exceptionName => 'PickFileCanceledException'; String get exceptionName => 'PickFileCanceledException';
@@ -1,7 +1,7 @@
import 'package:core/domain/exceptions/app_base_exception.dart'; import 'package:core/domain/exceptions/app_base_exception.dart';
class DataResponseIsNullException extends AppBaseException { class DataResponseIsNullException extends AppBaseException {
DataResponseIsNullException([super.message]); const DataResponseIsNullException([super.message]);
@override @override
String get exceptionName => 'DataResponseIsNullException'; String get exceptionName => 'DataResponseIsNullException';
@@ -29,7 +29,7 @@ class LocalFilePickerInteractor {
.toList(); .toList();
yield Right<Failure, Success>(LocalFilePickerSuccess(listFileInfo)); yield Right<Failure, Success>(LocalFilePickerSuccess(listFileInfo));
} else { } else {
yield Left<Failure, Success>(LocalFilePickerFailure(PickFileCanceledException())); yield Left<Failure, Success>(LocalFilePickerFailure(const PickFileCanceledException()));
} }
} catch (exception) { } catch (exception) {
yield Left<Failure, Success>(LocalFilePickerFailure(exception)); yield Left<Failure, Success>(LocalFilePickerFailure(exception));
@@ -26,7 +26,7 @@ class LocalImagePickerInteractor {
final fileInfo = filePickerResult!.files.first.toFileInfo(); final fileInfo = filePickerResult!.files.first.toFileInfo();
yield Right<Failure, Success>(LocalImagePickerSuccess(fileInfo)); yield Right<Failure, Success>(LocalImagePickerSuccess(fileInfo));
} else { } else {
yield Left<Failure, Success>(LocalImagePickerFailure(PickFileCanceledException())); yield Left<Failure, Success>(LocalImagePickerFailure(const PickFileCanceledException()));
} }
} catch (exception) { } catch (exception) {
yield Left<Failure, Success>(LocalImagePickerFailure(exception)); yield Left<Failure, Success>(LocalImagePickerFailure(exception));
+4 -8
View File
@@ -1,26 +1,22 @@
import 'package:equatable/equatable.dart';
import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart';
import 'package:core/domain/exceptions/app_base_exception.dart'; import 'package:core/domain/exceptions/app_base_exception.dart';
import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart';
class InvalidCapability extends AppBaseException with EquatableMixin { class InvalidCapability extends AppBaseException {
const InvalidCapability([super.message]); const InvalidCapability([super.message]);
@override @override
String get exceptionName => 'InvalidCapability'; String get exceptionName => 'InvalidCapability';
@override
List<Object?> get props => [message];
} }
class SessionMissingCapability extends InvalidCapability { class SessionMissingCapability extends InvalidCapability {
final Set<CapabilityIdentifier> capabilityIdentifiers; final Set<CapabilityIdentifier> capabilityIdentifiers;
const SessionMissingCapability(this.capabilityIdentifiers) SessionMissingCapability(this.capabilityIdentifiers)
: super('Missing capabilities $capabilityIdentifiers'); : super('Missing capabilities $capabilityIdentifiers');
@override @override
String get exceptionName => 'SessionMissingCapability'; String get exceptionName => 'SessionMissingCapability';
@override @override
List<Object?> get props => [capabilityIdentifiers, ...super.props]; List<Object?> get props => [capabilityIdentifiers];
} }
@@ -7,6 +7,9 @@ abstract class PermissionException with EquatableMixin implements Exception {
String get exceptionName; String get exceptionName;
@override
List<Object?> get props => [message];
@override @override
String toString() { String toString() {
if (message != null) { if (message != null) {
+2 -3
View File
@@ -110,17 +110,16 @@ class NoNetworkError extends RemoteException {
} }
class RefreshTokenFailedException extends RemoteException { class RefreshTokenFailedException extends RemoteException {
final int? statusCode;
RefreshTokenFailedException({ RefreshTokenFailedException({
super.message = super.message =
'Refresh Token failed with 400 Bad Request. The session is invalid/revoked.', 'Refresh Token failed with 400 Bad Request. The session is invalid/revoked.',
this.statusCode = 400, super.code = 400,
}); });
@override @override
String get exceptionName => 'RefreshTokenFailedException'; String get exceptionName => 'RefreshTokenFailedException';
@override @override
String toString() => "$exceptionName(status: $statusCode): $message"; String toString() => "$exceptionName(status: $code): $message";
} }
-1
View File
@@ -10,7 +10,6 @@ import 'package:core/presentation/utils/app_toast.dart';
import 'package:core/utils/app_logger.dart'; import 'package:core/utils/app_logger.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_appauth_web/authorization_exception.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:jmap_dart_client/jmap/core/error/method/error_method_response.dart'; import 'package:jmap_dart_client/jmap/core/error/method/error_method_response.dart';
import 'package:jmap_dart_client/jmap/core/error/method/exception/error_method_response_exception.dart'; import 'package:jmap_dart_client/jmap/core/error/method/exception/error_method_response_exception.dart';
@@ -45,7 +45,7 @@ void main() {
test('should rethrow exception when ServerSettingsAPI throws exception',() async { test('should rethrow exception when ServerSettingsAPI throws exception',() async {
// arrange // arrange
when(serverSettingsAPI.getServerSettings(any)) when(serverSettingsAPI.getServerSettings(any))
.thenThrow(NotFoundServerSettingsException()); .thenThrow(const NotFoundServerSettingsException());
// assert // assert
expect( expect(
@@ -71,7 +71,7 @@ void main() {
test('should rethrow exception when ServerSettingsAPI throws exception',() async { test('should rethrow exception when ServerSettingsAPI throws exception',() async {
// arrange // arrange
when(serverSettingsAPI.updateServerSettings(any, any, any)) when(serverSettingsAPI.updateServerSettings(any, any, any))
.thenThrow(CanNotUpdateServerSettingsException()); .thenThrow(const CanNotUpdateServerSettingsException());
// assert // assert
expect( expect(
@@ -38,7 +38,7 @@ void main() {
test('should rethrow exception when ServerSettingsDataSource throws exception', () async { test('should rethrow exception when ServerSettingsDataSource throws exception', () async {
// arrange // arrange
when(serverSettingsDataSource.getServerSettings(accountId)) when(serverSettingsDataSource.getServerSettings(accountId))
.thenThrow(NotFoundServerSettingsException()); .thenThrow(const NotFoundServerSettingsException());
// assert // assert
expect( expect(
@@ -64,7 +64,7 @@ void main() {
test('should rethrow exception when ServerSettingsDataSource throws exception', () async { test('should rethrow exception when ServerSettingsDataSource throws exception', () async {
// arrange // arrange
when(serverSettingsDataSource.updateServerSettings(session, accountId, serverSettings)) when(serverSettingsDataSource.updateServerSettings(session, accountId, serverSettings))
.thenThrow(NotFoundServerSettingsException()); .thenThrow(const NotFoundServerSettingsException());
// assert // assert
expect( expect(
@@ -39,7 +39,7 @@ void main() {
test('should return left with exception returned from repository', () { test('should return left with exception returned from repository', () {
// arrange // arrange
final exception = NotFoundServerSettingsException(); const exception = NotFoundServerSettingsException();
when(serverSettingsRepository.getServerSettings(any)).thenThrow(exception); when(serverSettingsRepository.getServerSettings(any)).thenThrow(exception);
// assert // assert
@@ -45,7 +45,7 @@ void main() {
test('should return left with exception returned from repository', () { test('should return left with exception returned from repository', () {
// arrange // arrange
final exception = NotFoundServerSettingsException(); const exception = NotFoundServerSettingsException();
when(serverSettingsRepository.updateServerSettings(any, any, any)) when(serverSettingsRepository.updateServerSettings(any, any, any))
.thenThrow(exception); .thenThrow(exception);