Files
Client/lib/api/errors/error_mapper.dart
T

115 lines
3.8 KiB
Dart

import 'dart:async';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:http/http.dart' as http;
import '../api_error.dart';
import '../marianumcloud/talk/talk_error.dart';
import '../webuntis/webuntis_error.dart';
import 'app_exception.dart';
import 'network_exception.dart';
import 'parse_exception.dart';
import 'server_exception.dart';
import 'talk_exception.dart';
import 'webuntis_exception.dart';
const String _defaultFallback = 'Etwas ist schiefgelaufen. Bitte versuche es erneut.';
const String _tlsErrorMessage =
'Die sichere Verbindung zum Server wurde abgelehnt (Zertifikat oder TLS-Fehler). '
'Häufige Ursachen: falsche Geräte-Uhrzeit oder ein WLAN mit Anmeldeseite (z.B. Café/Hotel).';
AppException? _dioToAppException(DioException error) {
switch (error.type) {
case DioExceptionType.connectionTimeout:
case DioExceptionType.sendTimeout:
case DioExceptionType.receiveTimeout:
return NetworkException.timeout(technicalDetails: error.message);
case DioExceptionType.connectionError:
return NetworkException(technicalDetails: error.message);
case DioExceptionType.badCertificate:
return const NetworkException(
userMessage: _tlsErrorMessage,
);
case DioExceptionType.badResponse:
final status = error.response?.statusCode;
return ServerException(
statusCode: status ?? -1,
technicalDetails: 'HTTP $status: ${error.message}',
);
case DioExceptionType.cancel:
case DioExceptionType.unknown:
final inner = error.error;
if (inner is SocketException) return NetworkException(technicalDetails: inner.message);
if (inner is HandshakeException) {
return const NetworkException(
userMessage: _tlsErrorMessage,
);
}
if (inner is FormatException) return ParseException(technicalDetails: inner.message);
return null;
}
}
String errorToUserMessage(Object? error, {String fallback = _defaultFallback}) {
if (error == null) return fallback;
if (error is AppException) return error.userMessage;
if (error is TalkError) return TalkException(error).userMessage;
if (error is WebuntisError) return WebuntisException(error).userMessage;
if (error is DioException) {
final mapped = _dioToAppException(error);
if (mapped != null) return mapped.userMessage;
}
if (error is SocketException) {
return const NetworkException().userMessage;
}
if (error is TimeoutException) {
return NetworkException.timeout().userMessage;
}
if (error is http.ClientException) {
return const NetworkException().userMessage;
}
if (error is HandshakeException) {
return _tlsErrorMessage;
}
if (error is FormatException) {
return const ParseException().userMessage;
}
if (error is ApiError) {
return _stripDioPrefix(error.message);
}
return fallback;
}
String? errorToTechnicalDetails(Object? error) {
if (error == null) return null;
if (error is AppException) return error.technicalDetails ?? error.toString();
if (error is TalkError) return TalkException(error).technicalDetails;
if (error is WebuntisError) return WebuntisException(error).technicalDetails;
if (error is DioException) {
final mapped = _dioToAppException(error);
if (mapped != null) return mapped.technicalDetails ?? mapped.toString();
}
return error.toString();
}
bool errorAllowsRetry(Object? error) {
if (error == null) return true;
if (error is AppException) return error.allowRetry;
if (error is DioException) {
final mapped = _dioToAppException(error);
if (mapped != null) return mapped.allowRetry;
}
return true;
}
String _stripDioPrefix(String raw) {
// ApiError messages embed full request URIs; only surface the first line.
final firstLine = raw.split('\n').first.trim();
return firstLine.isEmpty ? _defaultFallback : firstLine;
}