feat: Implement DataErrorHandler mixin and update imports for consistency
This commit is contained in:
@@ -6,7 +6,7 @@ import 'package:krow_core/core.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
|
||||
import '../../krow_data_connect.dart' as dc;
|
||||
import '../mixins/data_error_handler.dart';
|
||||
import 'mixins/data_error_handler.dart';
|
||||
|
||||
/// A centralized service for interacting with Firebase Data Connect.
|
||||
///
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
|
||||
/// Mixin to handle Data Layer errors and map them to Domain Failures.
|
||||
///
|
||||
/// Use this in Repositories to wrap remote calls.
|
||||
/// It catches [SocketException], [FirebaseException], etc., and throws [AppException].
|
||||
mixin DataErrorHandler {
|
||||
/// Executes a Future and maps low-level exceptions to [AppException].
|
||||
///
|
||||
/// [timeout] defaults to 30 seconds.
|
||||
Future<T> executeProtected<T>(
|
||||
Future<T> Function() action, {
|
||||
Duration timeout = const Duration(seconds: 30),
|
||||
}) async {
|
||||
try {
|
||||
return await action().timeout(timeout);
|
||||
} on TimeoutException {
|
||||
throw ServiceUnavailableException(
|
||||
technicalMessage: 'Request timed out after ${timeout.inSeconds}s');
|
||||
} on SocketException catch (e) {
|
||||
throw NetworkException(technicalMessage: 'SocketException: ${e.message}');
|
||||
} on FirebaseException catch (e) {
|
||||
final String code = e.code.toLowerCase();
|
||||
final String msg = (e.message ?? '').toLowerCase();
|
||||
if (code == 'unavailable' ||
|
||||
code == 'network-request-failed' ||
|
||||
msg.contains('offline') ||
|
||||
msg.contains('network') ||
|
||||
msg.contains('connection failed')) {
|
||||
throw NetworkException(
|
||||
technicalMessage: 'Firebase ${e.code}: ${e.message}');
|
||||
}
|
||||
if (code == 'deadline-exceeded') {
|
||||
throw ServiceUnavailableException(
|
||||
technicalMessage: 'Firebase ${e.code}: ${e.message}');
|
||||
}
|
||||
// Fallback for other Firebase errors
|
||||
throw ServerException(
|
||||
technicalMessage: 'Firebase ${e.code}: ${e.message}');
|
||||
} catch (e) {
|
||||
final String errorStr = e.toString().toLowerCase();
|
||||
if (errorStr.contains('socketexception') ||
|
||||
errorStr.contains('network') ||
|
||||
errorStr.contains('offline') ||
|
||||
errorStr.contains('connection failed') ||
|
||||
errorStr.contains('unavailable') ||
|
||||
errorStr.contains('handshake') ||
|
||||
errorStr.contains('clientexception') ||
|
||||
errorStr.contains('failed host lookup') ||
|
||||
errorStr.contains('connection error') ||
|
||||
errorStr.contains('grpc error') ||
|
||||
errorStr.contains('terminated') ||
|
||||
errorStr.contains('connectexception')) {
|
||||
throw NetworkException(technicalMessage: e.toString());
|
||||
}
|
||||
|
||||
// If it's already an AppException, rethrow it
|
||||
if (e is AppException) rethrow;
|
||||
|
||||
// Debugging: Log unexpected errors
|
||||
debugPrint('DataErrorHandler: Unhandled exception caught: $e');
|
||||
|
||||
throw UnknownException(technicalMessage: e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user