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:
Anton Stubenbord
2023-07-22 14:17:48 +02:00
parent c4f2810974
commit 6566b2b8d7
70 changed files with 1446 additions and 1133 deletions

View File

@@ -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;
}

View 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;
}

View 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);
}
}
}

View File

@@ -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',

View File

@@ -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,
));
}