mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-09 08:08:14 -06:00
feat: Rework error handling, upgrade dio, fixed bugs
- Fix grey screen bug when adding labels from documnet upload - Add more permission checks to conditionally show widgets
This commit is contained in:
@@ -1,99 +1,46 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/exception/server_message_exception.dart';
|
||||
import 'package:paperless_mobile/core/type/types.dart';
|
||||
|
||||
class DioHttpErrorInterceptor extends Interceptor {
|
||||
@override
|
||||
void onError(DioError err, ErrorInterceptorHandler handler) {
|
||||
void onError(DioException err, ErrorInterceptorHandler handler) {
|
||||
if (err.response?.statusCode == 400) {
|
||||
// try to parse contained error message, otherwise return response
|
||||
final dynamic data = err.response?.data;
|
||||
if (data is Map<String, dynamic>) {
|
||||
return _handlePaperlessValidationError(data, handler, err);
|
||||
} else if (data is String) {
|
||||
return _handlePlainError(data, handler, err);
|
||||
}
|
||||
} else if (err.response?.statusCode == 403) {
|
||||
var data = err.response!.data;
|
||||
if (data is Map && data.containsKey("detail")) {
|
||||
final data = err.response!.data;
|
||||
if (PaperlessServerMessageException.canParse(data)) {
|
||||
final exception = PaperlessServerMessageException.fromJson(data);
|
||||
final message = exception.detail;
|
||||
handler.reject(
|
||||
DioError(
|
||||
message: data['detail'],
|
||||
DioException(
|
||||
message: message,
|
||||
requestOptions: err.requestOptions,
|
||||
error: ServerMessageException(data['detail']),
|
||||
error: exception,
|
||||
response: err.response,
|
||||
type: DioErrorType.unknown,
|
||||
type: DioExceptionType.badResponse,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else if (err.error is SocketException) {
|
||||
final ex = err.error as SocketException;
|
||||
if (ex.osError?.errorCode == _OsErrorCodes.serverUnreachable.code) {
|
||||
return handler.reject(
|
||||
DioError(
|
||||
message: "The server could not be reached. Is the device offline?",
|
||||
error: const PaperlessServerException(ErrorCode.deviceOffline),
|
||||
} else if (PaperlessFormValidationException.canParse(data)) {
|
||||
final exception = PaperlessFormValidationException.fromJson(data);
|
||||
handler.reject(
|
||||
DioException(
|
||||
requestOptions: err.requestOptions,
|
||||
type: DioErrorType.connectionTimeout,
|
||||
error: exception,
|
||||
response: err.response,
|
||||
type: DioExceptionType.badResponse,
|
||||
),
|
||||
);
|
||||
} else if (data is String &&
|
||||
data.contains("No required SSL certificate was sent")) {
|
||||
handler.reject(
|
||||
DioException(
|
||||
requestOptions: err.requestOptions,
|
||||
type: DioExceptionType.badResponse,
|
||||
error:
|
||||
const PaperlessApiException(ErrorCode.missingClientCertificate),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
return handler.reject(err);
|
||||
}
|
||||
|
||||
void _handlePaperlessValidationError(
|
||||
Map<String, dynamic> json,
|
||||
ErrorInterceptorHandler handler,
|
||||
DioError err,
|
||||
) {
|
||||
final PaperlessValidationErrors errorMessages = {};
|
||||
for (final entry in json.entries) {
|
||||
if (entry.value is List) {
|
||||
errorMessages.putIfAbsent(
|
||||
entry.key,
|
||||
() => (entry.value as List).cast<String>().first,
|
||||
);
|
||||
} else if (entry.value is String) {
|
||||
errorMessages.putIfAbsent(entry.key, () => entry.value);
|
||||
} else {
|
||||
errorMessages.putIfAbsent(entry.key, () => entry.value.toString());
|
||||
}
|
||||
}
|
||||
handler.reject(
|
||||
DioError(
|
||||
error: errorMessages,
|
||||
requestOptions: err.requestOptions,
|
||||
type: DioErrorType.badResponse,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handlePlainError(
|
||||
String data,
|
||||
ErrorInterceptorHandler handler,
|
||||
DioError err,
|
||||
) {
|
||||
if (data.contains("No required SSL certificate was sent")) {
|
||||
handler.reject(
|
||||
DioError(
|
||||
requestOptions: err.requestOptions,
|
||||
type: DioErrorType.badResponse,
|
||||
error: const PaperlessServerException(
|
||||
ErrorCode.missingClientCertificate),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return handler.next(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum _OsErrorCodes {
|
||||
serverUnreachable(101);
|
||||
|
||||
const _OsErrorCodes(this.code);
|
||||
final int code;
|
||||
}
|
||||
|
||||
32
lib/core/interceptor/dio_offline_interceptor.dart
Normal file
32
lib/core/interceptor/dio_offline_interceptor.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
|
||||
class DioOfflineInterceptor extends Interceptor {
|
||||
@override
|
||||
void onError(DioException err, ErrorInterceptorHandler handler) {
|
||||
if (err.error is SocketException) {
|
||||
final ex = err.error as SocketException;
|
||||
if (ex.osError?.errorCode == _OsErrorCodes.serverUnreachable.code) {
|
||||
handler.reject(
|
||||
DioException(
|
||||
message: "The host could not be reached. Is your device offline?",
|
||||
error: const PaperlessApiException(ErrorCode.deviceOffline),
|
||||
requestOptions: err.requestOptions,
|
||||
type: DioExceptionType.connectionTimeout,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
handler.next(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum _OsErrorCodes {
|
||||
serverUnreachable(101);
|
||||
|
||||
const _OsErrorCodes(this.code);
|
||||
final int code;
|
||||
}
|
||||
27
lib/core/interceptor/dio_unauthorized_interceptor.dart
Normal file
27
lib/core/interceptor/dio_unauthorized_interceptor.dart
Normal file
@@ -0,0 +1,27 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
|
||||
class DioUnauthorizedInterceptor extends Interceptor {
|
||||
@override
|
||||
void onError(DioException err, ErrorInterceptorHandler handler) {
|
||||
if (err.response?.statusCode == 403) {
|
||||
final data = err.response!.data;
|
||||
String? message;
|
||||
if (PaperlessServerMessageException.canParse(data)) {
|
||||
final exception = PaperlessServerMessageException.fromJson(data);
|
||||
message = exception.detail;
|
||||
}
|
||||
handler.reject(
|
||||
DioException(
|
||||
message: message,
|
||||
requestOptions: err.requestOptions,
|
||||
error: PaperlessUnauthorizedException(message),
|
||||
response: err.response,
|
||||
type: DioExceptionType.badResponse,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
handler.next(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ class RetryOnConnectionChangeInterceptor extends Interceptor {
|
||||
});
|
||||
|
||||
@override
|
||||
void onError(DioError err, ErrorInterceptorHandler handler) async {
|
||||
void onError(DioException err, ErrorInterceptorHandler handler) async {
|
||||
if (_shouldRetryOnHttpException(err)) {
|
||||
try {
|
||||
handler.resolve(await DioHttpRequestRetrier(dio: dio)
|
||||
@@ -27,8 +27,8 @@ class RetryOnConnectionChangeInterceptor extends Interceptor {
|
||||
}
|
||||
}
|
||||
|
||||
bool _shouldRetryOnHttpException(DioError err) {
|
||||
return err.type == DioErrorType.unknown &&
|
||||
bool _shouldRetryOnHttpException(DioException err) {
|
||||
return err.type == DioExceptionType.unknown &&
|
||||
(err.error is HttpException &&
|
||||
(err.message?.contains(
|
||||
'Connection closed before full header was received',
|
||||
|
||||
@@ -8,7 +8,7 @@ class ServerReachabilityErrorInterceptor extends Interceptor {
|
||||
static const _missingClientCertText = "No required SSL certificate was sent";
|
||||
|
||||
@override
|
||||
void onError(DioError err, ErrorInterceptorHandler handler) {
|
||||
void onError(DioException err, ErrorInterceptorHandler handler) {
|
||||
if (err.response?.statusCode == 400) {
|
||||
final message = err.response?.data;
|
||||
if (message is String && message.contains(_missingClientCertText)) {
|
||||
@@ -19,7 +19,7 @@ class ServerReachabilityErrorInterceptor extends Interceptor {
|
||||
);
|
||||
}
|
||||
}
|
||||
if (err.type == DioErrorType.connectionTimeout) {
|
||||
if (err.type == DioExceptionType.connectionTimeout) {
|
||||
return _rejectWithStatus(
|
||||
ReachabilityStatus.connectionTimeout,
|
||||
err,
|
||||
@@ -48,13 +48,13 @@ class ServerReachabilityErrorInterceptor extends Interceptor {
|
||||
|
||||
void _rejectWithStatus(
|
||||
ReachabilityStatus reachabilityStatus,
|
||||
DioError err,
|
||||
DioException err,
|
||||
ErrorInterceptorHandler handler,
|
||||
) {
|
||||
handler.reject(DioError(
|
||||
handler.reject(DioException(
|
||||
error: reachabilityStatus,
|
||||
requestOptions: err.requestOptions,
|
||||
response: err.response,
|
||||
type: DioErrorType.unknown,
|
||||
type: DioExceptionType.unknown,
|
||||
));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user